【微信支付】APP支付和APIJS支付

前提须知

  1. APP支付流程链接
  2. jsapi支付流程链接
  3. jsapi的PHP SDK

实现

  • JsApi支付前先要重新获取下微信支付获取配置信息
//先验签 然后做逻辑
$appId = Config::get('app.WX_WEB_ID');
$appSecret = Config::get('app.WX_WEB_SECRET');
require($_SERVER['DOCUMENT_ROOT'].'/../vendor/simple/jssdk.php');//文件在SDK中
$jssdk = new \JSSDK($appId, $appSecret);//JSSDK中的参数是appid和appsecret。
$signPackage = $jssdk->GetSignPackage();//获取配置参数

return json(['code' => 200, 'data' => $signPackage, 'tips' => '']);
  • 统一下单部分(APP和JSAPI):

!!!! app统一下单 openID不是必填参数,当有微信登录的时候,可以利用存在本地的openID作为参数,如果没有微信登录,我们可以不用传openID,我们也能顺利下单,产生会话id:prepay_id,不要因此有什么疑惑。但是JSAPI统一下单,openID是必要参数,届时我们可以用code去获取openID信息,到时候具体问题具体分析。

$WxPayConfig = Config::get('app.WECHAT_PAY_CONFIG');
$jsApiPayConfig = Config::get('app.WECHAT_JSAPI_PAY_CONFIG');
$title = $data['title'];
$return_url = 'http://'.$_SERVER['HTTP_HOST'].'/api/v2/payment/wechatNotify';// 异步链接
$payData['appid'] = $WxPayConfig['APPID']; //应用ID
$payData['mch_id'] = $WxPayConfig['MCHID']; //商户号
$payData['nonce_str'] = md5(rand(100000,999999)); //随机字符串
$payData['sign_type'] = 'MD5'; //签名类型
$payData['body'] = $data['body']; //商品描述
$payData['out_trade_no'] = $data['out_trade_no']; //商户订单号
//$payData['fee_type'] = 'CNY'; //货币类型
$payData['total_fee'] = $data['total_fee'] * 100; //总金额(金额计数单位为:分)
//$payData['total_fee'] = 1; //总金额(金额计数单位为:分)
$payData['spbill_create_ip'] = '127.0.0.1'; //终端IP
$payData['notify_url'] = $return_url; //通知地址
$payData['trade_type'] = $tradeType; //交易类型
$signKey = $WxPayConfig['KEY'];
if($tradeType == 'JSAPI'){
    $payData['appid'] = $jsApiPayConfig['APPID'];
    $payData['mch_id'] = $jsApiPayConfig['MCHID'];
    $payData['openid'] = $openId;
    $signKey = $jsApiPayConfig['KEY'];
}

$payData['sign'] = $this->getWeixinSign($payData,$signKey); //签名
$xml = "<xml>";
foreach ($payData as $key=>$val)
{
    if (is_numeric($val)){
        $xml.="<".$key.">".$val."</".$key.">";
    }else{
        $xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
    }
}
$xml.="</xml>";

$url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
$ch = curl_init();
//设置超时
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch,CURLOPT_URL, $url);
//        curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE);
//        curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验

curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
//设置header
curl_setopt($ch, CURLOPT_HEADER, FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

//post提交方式
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
//运行curl
$data = curl_exec($ch);
//返回结果
if($data){
    curl_close($ch);
    $wxReturn = json_decode(json_encode(simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
    $prepay_id = $wxReturn['prepay_id'];
    if(!$prepay_id){
        //json_echo(1,"系统错误");exit;
        $json['code'] = 400;
        $json['_data_pay'] = $payData;
        $json['data'] = $data;
        $json['tips'] = $wxReturn['return_code'].$wxReturn['return_msg'];

        return $json;

    }
} else {
    $error = curl_errno($ch);
    curl_close($ch);
    $json['tips'] = $error;
    $json['code'] = 400;

    return $json;
}

$wechat = array();
if($tradeType == 'JSAPI'){
    //重中之重!!!!!!!网页端接口请求参数列表(参数需要重新进行签名计算,参与签名的参数为:appId、timeStamp、nonceStr、package、signType,参数区分大小写。) 大小写一定要一个字母不能错
    $wechat['appId'] = $wxReturn['appid'];
    $wechat['timeStamp'] = time();
    $wechat['nonceStr'] = $wxReturn['nonce_str'];
    $wechat['package'] = 'prepay_id='.$prepay_id;
    $wechat['signType'] = 'MD5';
}else{//参与签名的字段名为appid,partnerid,prepayid,noncestr,timestamp,package。注意:package的值格式为Sign=WXPay
    $wechat['appid'] = $wxReturn['appid'];
    $wechat['partnerid'] = $wxReturn['mch_id']; //商户号
    $wechat['prepayid'] = $prepay_id;
    $wechat['package'] = 'Sign=WXPay';
    $wechat['noncestr'] = $wxReturn['nonce_str'];
    $wechat['timestamp'] = time();
}

$wechat['sign'] = $this->getWeixinSign($wechat,$signKey);
if($tradeType == 'APP'){
    unset($wechat['package']);
}
$wechat['package_value'] = 'Sign=WXPay';
$wechat['result_scheme'] = $scheme;//scheme不参与加密  加密参数为强制几个参数

$json['code']= 200;
$json['data']= $wechat;
//记录日志
save_payment_log('wechat', $title,'unifiedorder', json_encode($json, FILE_APPEND));

return $json;

tips: 微信支付,同一订单修改价格后,重新提交订单号重复错误(201 商户订单号重复 解决方案)

举个栗子:

1、表内订单A为110110110110;

2、提交下单请求时,订单A拼接随机参数,如110110110110_0123;

3、接收返回信息,剔除订单号随机部分 _0123,得到表内订单A为110110110110;

4、该干嘛干嘛。

注意:

 最好记录提交到微信的单号 110110110110_0123,可能后续要进入微信后台进行财务查对。
  • 支付回调部分

微信支付回调部分传值是通过XML类型数据传递,所以接收到数据之后需要XML解析!

//处理微信支付回调
$testxml  = file_get_contents("php://input");  //接收微信发送的支付成功信息
$result = XMLDataParse($testxml);

/*$result = array(
    "appid" => "wx2421b1c4370ec43b",
    "attach" =>  "支付测试",
    "bank_type" =>"CFT",
    "fee_type" =>  "CNY",
    "is_subscribe" =>  "Y",
    "mch_id" =>  "10000100",
    "nonce_str" => "5d2b6c2a8db53831f7eda20af46e531c",
    "openid" =>  "oUpF8uMEb4qRXf22hE3X68TekukE",
    "out_trade_no" =>  "s129929113168",
    "result_code" =>  "SUCCESS",
    "return_code" => "SUCCESS",
    "sign" =>  "B552ED6B279343CB493C5DD0D78AB241",
    "sub_mch_id" =>  "10000100",
    "time_end" =>  "20140903131540",
    "total_fee" =>  "1",
    "coupon_fee" =>  "10",
    "coupon_count" =>  "1",
    "coupon_type" => "CASH",
    "coupon_id" => "10000",
    "coupon_fee_0" =>  "100",
    "trade_type" =>  "JSAPI",
    "transaction_id" => "1004400740201409030005092168",
);*/

save_payment_log('wechat', '微信支付回调开始','weChatNotify', json_encode($result, FILE_APPEND));
$sign_return = $result['sign'];
//如果成功返回了
if($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS') {
    if($result['trade_type'] == 'JSAPI'){
        $jsApiPayConfig = Config::get('app.WECHAT_JSAPI_PAY_CONFIG');
        $signKey = $jsApiPayConfig['KEY'];
    }else{
        $WxPayConfig = Config::get('app.WECHAT_PAY_CONFIG');
        $signKey = $WxPayConfig['KEY'];
    }


    unset($result['sign']);
    $sign = $this->getWeixinSign($result, $signKey);
    if ($sign == $sign_return) {
        //=================================验证签名后逻辑start===================================
        $payments = Db::table('payments')
            ->where('trade_no', $result['out_trade_no'])
            ->order('created_at', 'desc')
            ->select();
        if($payments){
            foreach ($payments as $payment){
                if($payment['paid']){//如果 paid为1 则已经完成
                    save_payment_log('wechat', '微信支付回调结束', 'weChatNotify', '支付单号为' . $result['out_trade_no'] . '支付回调失败:之前 已经有交易完成记录了!');

                    $str='<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
                    echo $str;exit();
                }
            }

            $status = $this->hadPaidChangeOrderStatus($result, 'wechat');//判断是否已经处理过(避免因为重复回调过程中出现问题)
            if($status){
                save_payment_log('wechat', '微信支付回调结束', 'weChatNotify', '支付单号为' . $result['out_trade_no'] . '支付回调成功:回调逻辑处理完毕!');

                $str='<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
                echo $str;exit();
            }

            save_payment_log('wechat', '微信支付回调结束', 'weChatNotify', '支付单号为' . $result['out_trade_no'] . '支付回调成功:订单状态修改失败!');
            $str='<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
            echo $str;exit();
        }else{
            save_payment_log('wechat', '微信支付回调结束', 'weChatNotify', '支付单号为' . $result['out_trade_no'] . '支付回调成功:未找到预支付单!');

            $str='<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
            echo $str;exit();
        }
        //=================================验证签名后逻辑end=====================================
    }

    save_payment_log('wechat', '微信支付回调结束', 'weChatNotify', '支付单号为' . $result['out_trade_no'] . '支付回调成功:sign验证失败!');
    $str='<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
    echo $str;exit();
}

save_payment_log('wechat', '微信支付回调结束', 'weChatNotify', '支付单号为' . $result['out_trade_no'] . '支付回调成功:支付失败!');

$str='<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
echo $str;exit();

tips微信加密算法:

 /**
  * @do 微信签名加密
  * @param 数据参数  加密key
  * @return 加密完数据
 */
 private function getWeixinSign($data,$key){
     ksort($data);
     $buff = "";
     foreach ($data as $k => $v){
         if($k != "sign" && $v != "" && !is_array($v)){
             $buff .= $k . "=" . $v . "&";
         }
     }
     $buff = trim($buff, "&") . "&key=".$key;
     $string = md5($buff);
     //签名步骤四:所有字符转为大写
     $result = strtoupper($string);
     
     return $result;
 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值