iOS&Android集成微信支付-Server篇(PHP)

iOS&Android集成微信支付-Server篇(PHP)

在上一篇文章《iOS集成微信支付-Swift版》中,我介绍了如何在iOS中集成微信支付。在支付过程中有一步是需要获取PrePay数据,至于如何获取,我会在本文后面详细介绍。在文章《手机App集成微信支付&支付宝-iOS&Android完整版》中,我介绍了整个支付流程,在App支付成功返回后,你的服务器并不知道相关的订单已经支付成功。这时候微信服务器会向你设置的服务器Notify URL发送POST请求,告诉你支付成功了。所以服务端主要需要实现的工作就是验证签名以及验证是否是微信发来的通知。

获取PrePay

微信支付(统一下单)文档中是这样描述的:

除被扫支付场景以外,商户系统先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易回话标识后再按扫码、JSAPI、APP等不同场景生成交易串调起支付。
关于获取预支付接口的请求参数、返回结果以及错误码请查看官方文档。有一点需要注意的是微信支付的接口参数用的是XML格式。下面就是获取PrePay的代码:

    $wxpay_config = $this->wxpay_config = $this->my_config['wxpay_config'];
    //var_dump($wxpay_config);

    $APP_ID = $wxpay_config['app_id'];            //APPID
    $APP_SECRET = $wxpay_config['app_secret'];    //appsecret
    $MCH_ID=$wxpay_config['mch_id'];
    $PARTNER_ID = $wxpay_config['partner_id'];
    $NOTIFY_URL = $wxpay_config['notify_url'];

    if (!$out_trade_no) {
        $this->display_error(400,'请求是无效的');
    }
    $order = $this->order_model->get_order_info($out_trade_no);
    if (!$order) {
        $this->display_error(1,'请求是无效的');
    }


    //STEP 1. 构造一个订单。
    $order=array(
        "body" => $order['Subject'],
        "appid" => $APP_ID,
        "device_info" => "APP-001",
        "mch_id" => $MCH_ID,
        "nonce_str" => mt_rand(),
        "notify_url" => $NOTIFY_URL,
        "out_trade_no" => $out_trade_no,
        "spbill_create_ip" => $this->input->ip_address(),
        "total_fee" => intval($order['TotalFee'] * 100),//注意:前方有坑!!!最小单位是分,跟支付宝不一样。1表示1分钱。只能是整形。
        "trade_type" => "APP"
    );
    ksort($order);

    //STEP 2. 签名
    $sign="";
    foreach ($order as $key => $value) {
        if($value&&$key!="sign"&&$key!="key"){
            $sign.=$key."=".$value."&";
        }
    }
    $sign.="key=".$PARTNER_ID;
    $sign=strtoupper(md5($sign));//echo $sign.'

';exit;

    //STEP 3. 请求服务器
    $xml="n";
    foreach ($order as $key => $value) {
        $xml.="<".$key.">".$value."n";
    }
    $xml.="".$sign."n";
    $xml.="";

    $opts = array(
        'http' =>
        array(
            'method'  => 'POST',
            'header'  => 'Content-type: text/xml',
            'content' => $xml
        ),
        "ssl"=>array(
            "verify_peer"=>false,
            "verify_peer_name"=>false,
        )
    );
    $context  = stream_context_create($opts);
    $result = file_get_contents('https://api.mch.weixin.qq.com/pay/unifiedorder', false, $context);

    $result = simplexml_load_string($result,null, LIBXML_NOCDATA);

    if ($result->return_code == 'SUCCESS' && $result->result_code == 'SUCCESS') {
        $prepay=array(
            "noncestr"=>"".$result->nonce_str,
            "prepayid"=>"".$result->prepay_id,//上一步请求微信服务器得到nonce_str和prepay_id参数。
            "appid"=>$APP_ID,
            "package"=>"Sign=WXPay",
            "partnerid"=>$MCH_ID,
            "timestamp"=>"".time(),
            "sign"=>""
        );
        ksort($prepay);
        $sign="";
        foreach ($prepay as $key => $value) {
            if($value&&$key!="sign"&&$key!="key"){
                $sign.=$key."=".$value."&";
            }
        }
        $sign.="key=".$PARTNER_ID;
        $sign=strtoupper(md5($sign));
        $prepay['sign'] = $sign;
        $prepay['success'] = true;
    } else {
        $prepay=array(
            "success" => false,
            "noncestr"=>"",
            "prepayid"=>"",
            "appid"=>$APP_ID,
            "package"=>"Sign=WXPay",
            "partnerid"=>$MCH_ID,
            "timestamp"=>"".time(),
            "sign"=>"",
            "return_msg"=>$result->return_msg
        );
    }


    $this->response($prepay, 200);

此处有一个坑需要注意,微信支付接口的金额最小单位是分,跟支付宝不一样,1表示1分钱,且只能是整形。
客户端App通过该API获取到PrePay信息有就可以发起支付请求了。客户端的实现代码请查看文章《iOS&Android集成微信支付-Server篇》

Notify URL

前面说过,这个URL是支付成功后微信调用的。那么微信Server是如何知道这个URL的呢?其实是在前面请求PrePay的时候通过参数notify_url传过去的。
微信调用支付结果通知的详细参数请访问文档「支付结果通用通知」。同样,参数格式也是XML。
所有这些参数中,我们最关心的应该是下面几个:
return_code :返回状态码。SUCCESS/FAIL
result_code : 交易标识,交易是否成功 SUCCESS/FAIL
out_trade_no : 商户订单号, 需要在你的数据库能够唯一标志订单
transaction_id :微信支付订单号,流水号。记录到你的数据库方便对账用
获取到这些信息后就可以从自己系统DB里面通过out_trade_no查询出相应的订单,标记为支付成功,并记录下相应的交易流水号transaction_id. 需要注意的是,处理玩业务逻辑后需要返回"return_code"为"success"给微信服务器。

Notify实现代码如下:

/**
 * 微信支付Notify
 */
function wxpay_notify(){
    //使用通用通知接口
    $notify = new Notify_pub();

    //存储微信的回调
    $xml = $GLOBALS['HTTP_RAW_POST_DATA'];
    $notify->saveData($xml);

    //验证签名,并回应微信。
    //对后台通知交互时,如果微信收到商户的应答不是成功或超时,微信认为通知失败,
    //微信会通过一定的策略(如30分钟共8次)定期重新发起通知,
    //尽可能提高通知的成功率,但微信不保证通知最终能成功。
    if($notify->checkSign() == FALSE){
        $notify->setReturnParameter("return_code","FAIL");//返回状态码
        $notify->setReturnParameter("return_msg","签名失败");//返回信息
    }else{
        $notify->setReturnParameter("return_code","SUCCESS");//设置返回码
    }
    $returnXml = $notify->returnXml();
    echo $returnXml;

    //==商户根据实际情况设置相应的处理流程,此处仅作举例=======

    //以log文件形式记录回调信息

    $log_type="wxpay_notify";//log文件路径
    $this->log_result($log_type,"【接收到的notify通知】:n".$xml."n");

    if($notify->checkSign() == TRUE) {
        if ($notify->data["return_code"] == "FAIL") {
            //此处应该更新一下订单状态,商户自行增删操作
            $this->log_result($log_type,"【通信出错】:n".$xml."n");
        } elseif ($notify->data["result_code"] == "FAIL") {
            //此处应该更新一下订单状态,商户自行增删操作
            $this->log_result($log_type,"【业务出错】:n".$xml."n");
        } else {
            //此处应该更新一下订单状态,商户自行增删操作
            $this->log_result($log_type,"【支付成功】:n".$xml."n");
            $out_trade_no = $notify->data['out_trade_no'];
            $trade_no = $notify->data['transaction_id'];

            $order = $this->order_model->get_order_info($out_trade_no);
            if($order['TradeStatus'] != 'TRADE_FINISHED' && $order['TradeStatus'] != 'TRADE_SUCCESS'){
                $data = array('TradeStatus'=>'TRADE_SUCCESS','TradeNo'=>$trade_no,'PayTime'=>time(),'PayType'=>'wxpay');
                $this->order_model->update_order_info($out_trade_no,$data);
            }
        }

    }

}

完整的实现代码请移步Github下载
Server端是基于CI框架实现的,如果你不熟悉的话也没有关系,下载demo后打开 /application/config/myconfig.php 文件,将数组 wxpay_config 中的 app_id,app_secret,mch_id, partner_id,notify_url,替换成你自己的配置即可运行。
其中 app_id,app_secret 是你在微信开放平台创建的应用。
mch_id为「微信支付商户号」,登录微信支付商户平台即可看到。

微信支付商户号

在本文章Demo实现过程中,我没有采用任何安全验证措施,在实际开放过程中Server端API是需要根据自身情况采取相应安全策略的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352

推荐阅读更多精彩内容