之前在开发APP中用到了微信支付,因为是第一次用,所以中途也遇到了好多问题,通过查看文档和搜集资料,终于完成了该功能的实现。在这里简单分享一下后台php接口的开发实例。

原文地址:代码汇个人博客 http://www.codehui.net/info/4.html

开发流程

1:用户在商户APP中选择商品,提交订单,选择微信支付。

2:商户后台收到用户支付单,调用微信支付统一下单接口。参见【统一下单API】。

3:统一下单接口返回正常的prepay_id,再按签名规范重新生成签名后,将数据传输给APP。参与签名的字段名为appid,partnerid,prepayid,noncestr,timestamp,package。注意:package的值格式为Sign=WXPay

4:商户APP调起微信支付。api参见本章节【app端开发步骤说明

5:商户后台接收支付通知。api参见【支付结果通知API

6:商户后台查询支付结果。,api参见【查询订单API

开发中

首先呢我们需要拿到三个参数(appid,mch_id,key),这三个参数分别对应的是 在微信开发平台中创建的移动应用appid,微信支付商户号商户支付秘钥,详情看参考【支付结果通知API

然后我们先把统一下单所需要的参数列出来

$request_data = array(
'appid' => C('WX_APPID'), #应用APPID
'mch_id' => C('WX_MCHID'), #商户号
'trade_type' => 'APP', #支付类型
'nonce_str' => \Org\Util\String::randString(30), #随机字符串 不长于32位
'body' => '商品名称', #商品名称
'out_trade_no' => '12345678912456', #商户后台订单号
'total_fee' => '1', #商品价格
'spbill_create_ip' => get_client_ip(), #用户端实际ip
'notify_url' => 'http://***/app/index.php/Home/Wxpay/wx_notify', #异步通知回调地址
);

这些都是请求参数必填项,其他参数请查看文档

下来我们就要使用这些参数生成签名了

$request_data['sign'] = $this -> get_sign($request_data);

我们下来需要把微信请求的数据拼装成 xml格式,注意:xml数据要使用<![CDATA[]]>包括

$xml_data = $this -> set_xmldata($request_data);

打印$xml_data结果如下

<xml>
<appid><![CDATA[wx7ad3cc6c6111111]]></appid>
<mch_id><![CDATA[1494741111]]></mch_id>
<trade_type><![CDATA[APP]]></trade_type>
<nonce_str><![CDATA[WXXWkMDOgLIqhUnITfNrBbJEVGQdRO]]></nonce_str>
<body><![CDATA[u5546u54c1u540du79f0]]></body>
<out_trade_no><![CDATA[12345678912456]]></out_trade_no>
<total_fee><![CDATA[1]]></total_fee>
<spbill_create_ip><![CDATA[1.86.242.193]]></spbill_create_ip>
<notify_url><![CDATA[http://***/app/index.php/Home/Wxpay/wx_notify]]></notify_url>
<sign><![CDATA[EC0BFB3434A72F20C2CA3378BF07264C]]></sign>
</xml>

现在就可以向微信发送请求了

$res = $this -> send_prePaycurl($xml_data);

这是请求的返回值

{
return_code: "SUCCESS", #业务结果 只有这里返回SUCCESS才会有prepay_id
return_msg: "OK", #返回结果描述
appid: "wx7ad3cc6c6111111", #应用APPID
mch_id: "1494741111", #商户号
nonce_str: "jkh9mmRlmSHBJxO0", #随机字符串
sign: "AF3B26B1E58591D6565E61DDFBB7837B", #签名
result_code: "SUCCESS", #也是业务结果
prepay_id: "wx20171226005556c5c65b325a0132782836", #预支付交易会话标识,用于APP请求微信支付调用,有效期两小时
trade_type: "APP" #支付类型
}

到这里拿到prepay_id还没完我们还需要对返回的数据进行二次签名

if($res['return_code'] == 'SUCCESS' && $res['result_code'] == 'SUCCESS'){
$two_data['appid'] = C('WX_APPID'); #APPID
$two_data['partnerid'] = C('WX_MCHID'); #商户号
$two_data['prepayid'] = $res['prepay_id']; //预支付交易会话标识
$two_data['noncestr'] = \Org\Util\String::randString(30);
$two_data['timestamp'] = time(); #时间戳
$two_data['package'] = "Sign=WXPay"; #固定值
$two_data['sign'] = $this -> get_twosign($two_data); #二次签名
$this->ajaxReturn(array('code'=>200,'info'=>$two_data));
}else{
$this->ajaxReturn(array('code'=>201,'info'=>$res['err_code_des']));
}

然后就可以在商户APP端通过prepayid进行支付了

下面我们来列出上面调用的几个公共方法

    //一次签名的函数
private function get_sign($data){
ksort($data);
$str = '';
foreach ($data as $key => $value) {
$str .= !$str ? $key . '=' . $value : '&' . $key . '=' . $value;
}
$str.='&key='.C('WX_KEY');
$sign = strtoupper(md5($str));
return $sign;
}
//二次签名的函数
private function get_twosign($data){
$sign_data = array(
"appid"=>$data['appid'],
"partnerid"=>$data['partnerid'],
"prepayid"=>$data['prepayid'],
"noncestr"=>$data['noncestr'],
"timestamp"=>$data['timestamp'],
"package"=>$data['package'],
);
return $this -> get_sign($sign_data);
}
//生成xml格式的函数
private function set_xmldata($data) {
$xmlData = "<xml>";
foreach ($data as $key => $value) {
$xmlData.="<".$key."><![CDATA[".$value."]]></".$key.">";
}
$xmlData = $xmlData."</xml>";
return $xmlData;
}
//通过curl发送数据给微信接口的函数
private function send_prePaycurl($xmlData) {
$url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
$header[] = "Content-type: text/xml";
$curl = curl_init();
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $xmlData);
$data = curl_exec($curl);
if (curl_errno($curl)) {
print curl_error($curl);
}
curl_close($curl);
return $this -> _xmldataparse($data);
}
//xml数据解析函数
private function _xmldataparse($data){
$msg = array();
$msg = (array)simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA);
return $msg;
}

微信回调

支付有了,肯定还得有回调

    //微信回调
public function wx_notify(){
//允许从外部加载XML实体(防止XML注入攻击)
libxml_disable_entity_loader(true);
$postStr = $this -> post_data(); #接收微信返回数据xml格式
$postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA); #xml格式数据转换成对象
$arr = $this -> object_toarray($postObj); #对象转成数组
ksort($arr); # 对数据进行排序
$str = $this -> params_tourl($arr); #对数据拼接成字符串
$user_sign = strtoupper(md5($str)); //把微信返回的数据进行再次签名
//验证签名
if($user_sign == $arr['sign']){
//验证签名成功 处理商户订单逻辑
//给微信返回接收到数据通知
return '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
}else{
//签名验证失败 微信会再次访问回调方法
return '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
}
}

回调用到的方法如下

// 接收post数据
/*
* 微信是用$GLOBALS['HTTP_RAW_POST_DATA'];这个函数接收post数据的
*/
public function post_data(){
$receipt = $_REQUEST;
if($receipt==null){
$receipt = file_get_contents("php://input");
if($receipt == null){
$receipt = $GLOBALS['HTTP_RAW_POST_DATA'];
}
}
return $receipt;
} //把对象转成数组
public function object_toarray($arr) {
if(is_object($arr)) {
$arr = (array)$arr;
} if(is_array($arr)) {
foreach($arr as $key=>$value) {
$arr[$key] = $this->object_toarray($value);
}
}
return $arr;
} /**
* 格式化参数格式化成url参数
*/
private function params_tourl($arr)
{
$weipay_key = C('WX_KEY');//微信的key,这个是微信支付给你的key,不要瞎填。
$buff = "";
foreach ($arr as $k => $v)
{
if($k != "sign" && $v != "" && !is_array($v)){
$buff .= $k . "=" . $v . "&";
}
}
$buff = trim($buff, "&");
return $buff.'&key='.$weipay_key;
}

总结:首先微信支付的流程比较多,公众号开放平台微信商户,配置参数的时候要看仔细,不要后面的坑特别多,因为是第一次写微信支付,可能会存在部分问题,欢迎大家可以在下面留言反馈。

这是最后完成的功能



下面分享一下全部的代码

<?php
namespace Home\Controller;
use Think\Controller;
/**
* php开发微信app支付接口
* @global WX_APPID 开放平台->移动应用appid
* @global WX_MCHID 微信支付商户号
* @global WX_KEY 商户支付秘钥
* @author codehi <admin@codehui.net> 2017-12-23
*/
class WxpayController extends Controller
{
/**
* 微信支付统一下单 >>> 生成预支付交易单
*/
public function wx_pay(){ $request_data = array(
'appid' => C('WX_APPID'), #应用APPID
'mch_id' => C('WX_MCHID'), #商户号
'trade_type' => 'APP', #支付类型
'nonce_str' => \Org\Util\String::randString(30), #随机字符串 不长于32位
'body' => '商品名称', #商品名称
'out_trade_no' => '12345678912456', #商户后台订单号
'total_fee' => '1', #商品价格
'spbill_create_ip' => get_client_ip(), #用户端实际ip
'notify_url' => 'http://shop.lsmrsd.com/app/index.php/Home/Wxpay/wx_notify', #异步通知回调地址
);
// 获取签名
$request_data['sign'] = $this -> get_sign($request_data);
// 拼装数据
$xml_data = $this -> set_xmldata($request_data); // 发送请求
$res = $this -> send_prePaycurl($xml_data);
$this->ajaxReturn($res);
if($res['return_code'] == 'SUCCESS' && $res['result_code'] == 'SUCCESS'){
$two_data['appid'] = C('WX_APPID'); #APPID
$two_data['partnerid'] = C('WX_MCHID'); #商户号
$two_data['prepayid'] = $res['prepay_id']; //预支付交易会话标识
$two_data['noncestr'] = \Org\Util\String::randString(30);
$two_data['timestamp'] = time();
$two_data['package'] = "Sign=WXPay";
$two_data['sign'] = $this->get_twosign($two_data);
$this->ajaxReturn(array('code'=>200,'info'=>$two_data));
}else{
$this->ajaxReturn(array('code'=>201,'info'=>$res['err_code_des']));
}
} //通过curl发送数据给微信接口的函数
private function send_prePaycurl($xmlData) {
$url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
$header[] = "Content-type: text/xml";
$curl = curl_init();
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $xmlData);
$data = curl_exec($curl);
if (curl_errno($curl)) {
print curl_error($curl);
}
curl_close($curl);
return $this->_xmldataparse($data);
} //xml数据解析函数
private function _xmldataparse($data){
$msg = array();
$msg = (array)simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA);
return $msg;
} //生成xml格式的函数
private function set_xmldata($data) {
$xmlData = "<xml>";
foreach ($data as $key => $value) {
$xmlData.="<".$key."><![CDATA[".$value."]]></".$key.">";
}
$xmlData = $xmlData."</xml>";
return $xmlData;
} //一次签名的函数
private function get_sign($data){
ksort($data);
$str = '';
foreach ($data as $key => $value) {
$str .= !$str ? $key . '=' . $value : '&' . $key . '=' . $value;
}
$str.='&key='.C('WX_KEY');
$sign = strtoupper(md5($str));
return $sign;
} //二次签名的函数
private function get_twosign($data){
$sign_data = array(
"appid"=>$data['appid'],
"partnerid"=>$data['partnerid'],
"prepayid"=>$data['prepayid'],
"noncestr"=>$data['noncestr'],
"timestamp"=>$data['timestamp'],
"package"=>$data['package'],
);
return $this->get_sign($sign_data);
} //微信回调
public function wx_notify(){
//允许从外部加载XML实体(防止XML注入攻击)
libxml_disable_entity_loader(true);
$postStr = $this -> post_data();//接收post数据
$postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
$arr = $this -> object_toarray($postObj);//对象转成数组
ksort($arr);// 对数据进行排序
$str = $this -> params_tourl($arr);//对数据拼接成字符串
$user_sign = strtoupper(md5($str));
if($user_sign == $arr['sign']){//验证签名
return '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
}else{
return '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
}
} // 接收post数据
/*
* 微信是用$GLOBALS['HTTP_RAW_POST_DATA'];这个函数接收post数据的
*/
public function post_data(){
$receipt = $_REQUEST;
if($receipt==null){
$receipt = file_get_contents("php://input");
if($receipt == null){
$receipt = $GLOBALS['HTTP_RAW_POST_DATA'];
}
}
return $receipt;
} //把对象转成数组
public function object_toarray($arr) {
if(is_object($arr)) {
$arr = (array)$arr;
} if(is_array($arr)) {
foreach($arr as $key=>$value) {
$arr[$key] = $this->object_toarray($value);
}
}
return $arr;
} /**
* 格式化参数格式化成url参数
*/
private function params_tourl($arr)
{
$weipay_key = C('WX_KEY');//微信的key,这个是微信支付给你的key,不要瞎填。
$buff = "";
foreach ($arr as $k => $v)
{
if($k != "sign" && $v != "" && !is_array($v)){
$buff .= $k . "=" . $v . "&";
}
}
$buff = trim($buff, "&");
return $buff.'&key='.$weipay_key;
} }

php开发微信APP支付接口的更多相关文章

  1. .net 微信APP支付接口的开发流程以及坑

    流程 申请APP的微信支付 申请成功之后得到APPID 商户号 以及自己设置商户号的支付密码 这时就可以开发接口了 微信APP支付API:https://pay.weixin.qq.com/wiki/ ...

  2. 开发微信App支付

    1.首先到官方下载Demo,地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1 下载后的目录结构如下:

  3. 微信app支付android客户端以及.net服务端实现

    由于公司运营需要,需要在客户端(android/ios)增加微信以及支付宝支付,在调用微信app支付时遇到一些问题,也算是一些踩过的坑,记录下来 ,希望能对.net开发者服务端网站更快的集成微信app ...

  4. H5使用codovar插件实现微信支付(微信APP支付模式,前端)

    H5打包的app实现微信支付及支付宝支付,本章主要详解微信支付,支付宝支付请查看另一篇“H5使用codovar插件实现支付宝支付(支付宝APP支付模式,前端)” ps:本文只试用H5开发的,微信 AP ...

  5. 微信支付-微信公众号支付,微信H5支付,微信APP支付,微信扫码支付

    在支付前,如果使用第三方MVC框架,则使用重写模式,服务器也需要配置该项 if (!-e $request_filename){ rewrite ^/(.*)$ /index.php/$ last; ...

  6. Android版-微信APP支付

    首发地址: Android版-微信APP支付 欢迎留言.转发 微信极速开发系列文章(微信支付.授权获取用户信息等):点击这里 目录 1.注册账号.开发者认证 2.添加应用 3.申请微信支付 4.技术开 ...

  7. 微信APP支付整体流程记录备忘

      支付整体流程见文档:https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=8_3   商户系统和微信支付系统主要交互说明:     步骤1: ...

  8. 微信App支付接入步骤&支付中前后端交互流程

    最近对微信App支付(App端集成微信支付SDK)申请步骤,以及终端在进行微信支付时商户App.商户Server.微信App.微信支付Server的交互流程进行了简单了解.这篇文章应该算是学习笔记,分 ...

  9. 微信app支付 ci框架做的

    /**     * 组合微信app支付  获得prepayid     * @param int $order_num     */    private function _wxpay_reques ...

随机推荐

  1. IO复用(较详细)

    进程与线程的描述 一个进程至少会创建一个线程,多个线程共享一个程序进程的内存.程序的运行最终是靠线程来完成操作的.线程的数量跟CPU核数有关,一个核最多能发出两个线程.线程的操作主要分为:一:给CPU ...

  2. 关于 redis.properties配置文件及rule

    当你配置的 FinanceRlue 的path为/count/users时,redis.properties中就必须也配置一个/users=redis://localhost:6379/2?keyle ...

  3. [Abp 源码分析]四、模块配置

    0.简要介绍 在 Abp 框架当中通过各种 Configuration 来实现模块的配置,Abp 本身提供的很多基础设施功能的一些在运行时的行为是通过很多不同的 Configuration 来开放给用 ...

  4. qt 多语化

        最近项目使用的qt版本升级,导致了界面乱码问题,因此最后决定利用qt的多语化机制,来解决乱码问题,首先感谢这两篇文字的帮助,在此加上作者链接:Qt之多语化和Qt多国语言的实现与切换(国际化) ...

  5. 『Two 树的直径求解及其运用』

    树的直径 我们先来认识一下树的直径. 树是连通无环图,树上任意两点之间的路径是唯一的.定义树上任意两点\(u, v\)的距离为\(u\)到\(v\)路径上边权的和.树的直径\(MN\)为树上最长路径, ...

  6. RabbitMQ学习笔记(一) Hello World

    RabbitMQ是做什么的? RabbitMQ可以类比现实生活中的邮政服务. 现实中邮件服务处理的是邮件,发件人写好信件投入邮箱,邮递员收取信件存入邮局,邮局根据信件地址,分配邮递员投递信件到指定地点 ...

  7. nginx系列 3 nginx.conf介绍(1)

    一. nginx.conf 文件结构概述 在第一篇中讲到nginx的安装,安装完后,默认的nginx服务器配置文件都存在安装目录conf中,主配置文件名为nginx.conf.下面是我linux系统安 ...

  8. 初识Shiro

    Shiro是Apache基金会下的一个开源安全框架,提供了身份验证.授权.密码学和会话管理等功能,Shiro框架不仅直观易用,而且也能提供健壮的安全性,另外一点值得说的是Shiro的前身是一个始于20 ...

  9. Android Native crash日志分析

    在Android应用crash的类型中,native类型crash应该是比较难的一种了,因为大家接触的少,然后相对也要多转几道工序,所有大部分对这个都比较生疏.虽然相关文章也有很多了,但是我在刚开始学 ...

  10. 为了学好Java,我尝试了这 6 个方法

    阅读本文大概需要 5 分钟. 教练,我想学Java! 怎么学Java,一个简单的命题,我自己也折腾了好几年,现在虽不能说是Java高手,但也算是小有所成,至少还不至于搞不懂一些基本概念和技术原理. 从 ...