微信JSAPI支付那点事

准备工作

微信商户账户/密码(获取appid等信息)
微信公众号账户/密码(获取cert证书等信息,不做线上退款不需要证书)
基础php知识
自己已备案的域名(备案后微信不拦截)
自己可登陆的服务器(上传代码用)

下载php支付demo

从商户平台进入的话是以下界面或者直接搜索公众号支付文档

支付类型

https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_1

payType.png

php-demo下载

https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1

php-demo.png

修改部分文件并预览index.php

解压文件,打开index.php修改
可以改成绝对路径,即你放的位置(某个域名或者某个域名下某个文件夹下)或者相对路径

http://'.$_SERVER['HTTP_HOST'].'/WxpayAPI/example/jsapi.php
path.png
更改配置(整个demo最重要的修改的地方)
config.png

说明
appId:是你的微信公众平台的appId
appsecret:是你商户平台的appsecret这两个参数可登录你的微信公众平台查看
mchId:是你的商户Id,这个是你的微信公众号开通微信支付,微信分配给你的一个商户支付ID,可登陆商户平台查看,即是你登录商户平台的账号
key:是你商户平台的支付秘钥,这个参数很关键,它是一个32位的字符串

一定要保证你以上四个参数的正确,不然出现的各种错误皆可能是这四个参数不正确造成的,当然前提是你开通微信支付并且配置正确的授权域名和支付目录

修改异步通知地址

打开example文件夹下的jsapi.php

$input->SetNotify_url("http://www.xxx.com/WxpayAPI/example/notify.php"); //设置接收微信支付异步通知回调地址

改为你自己的地址,可以写死或者相对路径获取该地址

添加异步通知地址配置

Wxpay.api.php中的unifiedOrder下单方法中需要

//异步通知url未设置,则使用配置文件中的url

    if(!$inputObj->IsNotify_urlSet()){
        $inputObj->SetNotify_url(WxPayConfig::NOTIFY_URL);//异步通知url
    }

所以如果你没设置,则需要去WXPay.Config.php中添加一条:

const NOTIFY_URL='https://www.xxx.com/WxpayAPI/example/notify.php';
配置授权域名

因为微信支付要获取openId,它是微信支付用户的凭证

auth.png
配置支付目录
mchId.png
上传整个文件夹到你写的支付目录下的域名下

比如你的是www.xxx.com/WxpayAPI/example/
那么你就把你整个WxpayAPI文件夹传到你的www.xxx.com域名目录下
预览index.php

index.png

确认你的商户信息
pay.png
重点(坑点)

支付成功jsapi中的js就可以根据返回的信息跳转到支付成功的页面,这是接收微信同步返回的处理

function jsApiCall()
{
    WeixinJSBridge.invoke(
        'getBrandWCPayRequest',
         <?php echo $jsApiParameters; ?>, //订单参数
         function (res) {
                alert(res.err_msg);
                WeixinJSBridge.log(res.err_msg);
                if (res.err_msg == 'get_brand_wcpay_request:ok') {
                      window.location.href = 'http://h.yiwang.com/index.php?r=cart/finishedorder&paymentId='+<?php echo $paymentId;?>+'&orderId='+<?php echo $orderId;?>;
                }else if (res.err_msg == 'get_brand_wcpay_request:cancel') {
                      alert('cancel');
                } else {
                      alert('fail');
                      alert(res.err_msg);
                }
           }
      );
   }

但是最终是否支付成功不能以这个为准,而是notify.php接收到的异步通知为准,这是微信异步返回的结果处理

public function NotifyProcess($data, &$msg)
{
    Log::DEBUG("call back:" . json_encode($data));
    $notfiyOutput = array();
    if(!array_key_exists("transaction_id", $data)){
        $msg = "输入参数不正确";
        return false;
    }
    //查询订单,判断订单真实性
    if(!$this->Queryorder($data["transaction_id"])){
        $msg = "订单查询失败";
        return false;
    }
    $this->UpdateOrderStatus();//自定义更改数据库支付成功状态
    return true;
}

具体的更改数据库订单状态需要接收到微信异步通知的xml,然后判断是否支付成功,再进行更改

function UpdateOrderStatus() {
      $string = file_get_contents("php://input");//微信返回的xml支付结果
      $arr = (array) simplexml_load_string($string, 'SimpleXMLElement', LIBXML_NOCDATA);
      if ($arr['result_code'] == 'SUCCESS' && $arr['return_code'] == 'SUCCESS') {
           //操作数据库处理订单状态等
      }
}

以上是分析的demo大致流程,接下来我说一下我遇到的坑,望各位注意:

坑一:签名错误

这是我最开始调试SDK的demo时候第一个坑
我们公司把已经使用是APP的商户Id和key给我,让我用这个,这个后台可以登录,我看到了商户id没问题,但key看不到,只有重置才可以,因为线上用着,我也没法重置,还以为代码有问题,各种输出查看。
百度好多人都让重置key,说同一个key重新提交一次也行,有人提交两三次同一个key,说就不报签名无效了。但我们没有开发环境,我不能任性重置。只能找代码的问题

最终发现还是第一步的四个配置参数有问题,appId没问题是公众号的id,我没用他们给我的appId,他们的appId应该是App开放平台的appId

商户id和秘钥,给我的是app的商户id和key,其实不是。应该是我们微信公众号申请微信支付分配的商户id和秘钥。而不是说用APP申请的商户id和秘钥。
虽然文档说的很清楚,我也知道,但领导就说和app共用一个,我也很无奈。因为开始我也不是很清楚,就觉得那就是吧,或许是demo有问题(哈哈哈)

坑二:out_trade_no的最少位数问题

微信说是32位以内,但是,如果你传1位,肯定会报错,它的提示还很弱,不是说位数不足,具体什么提示我忘了。反正位数太少会出错,我在这里也是摔了个跟头

坑三:不知道notify.php这个文件怎么接收微信支付返回结果

搜了好多资料,大部分人都只讲解了demo,说支付成功以异步通知为准,微信是通知给你设置的地址了,但问题是我怎么获取他的通知呢?

总是觉得没有返回变量,就不知道怎么输出变量,怎么处理。

$string = file_get_contents("php://input");

这就是接收到微信的通知,一个xml,详细代码上边有

坑四:jsapi.php接收不到上一个页面post或get传过来的参数

原因:在创建商户订单时,需要获取到用户的openid

//①、获取用户openid
$tools = new JsApiPay();
$openId = $tools->GetOpenid();

Wxpay.JsApiPay.php
但是在获取用户openid的过程中需要请求CODE,CODE请求函数如下:

public function GetOpenid()
{
    //通过code获得openid
    if (!isset($_GET['code'])){
        //触发微信返回code码
        $baseUrl = urlencode('http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'].$_SERVER['QUERY_STRING']);
        $url = $this->__CreateOauthUrlForCode($baseUrl);
        Header("Location: $url");
        exit();
    } else {
        //获取code码,以获取openid
        $code = $_GET['code'];
        $openid = $this->getOpenidFromMp($code);
        return $openid;
    }
}

因此,在获取openid商户后台与微信系统进行多次交互,当再次回到生成商户订单页的时候,我们之前从h5页面通过$_GET或者$_POST方式获取的参数已经被清空,所以无法生成商户订单。
解决办法:
1.先将用户h5页面提交过来的参数保存到session中,然后再重定向到要生成商户订单的页面,
在生成商户订单的页面中先获取openid,再获取session
中的值(这是我百度到别人采用的方法)
2.我的办法:
在获取openid之前把post或者get的参数取到,然后传给微信(采用和他获取code的机制一样)

//①、获取用户openid
$tools = new JsApiPay();
$arr = $tools->GetOpenid($Out_trade_no,$paymentId,$Total_fee);

重写GetOpenid方法,如下:

public function GetOpenid($orderId,$paymentId,$totalFee)
{
    //通过code获得openid
    if (!isset($_GET['code'])){
        //触发微信返回code码
        $baseUrl = urlencode('http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'].$_SERVER['QUERY_STRING'].'?orderId='.$orderId.'&paymentId='.$paymentId.'&totalFee='.$totalFee);
        $url = $this->__CreateOauthUrlForCode($baseUrl);
        Header("Location: $url");
        exit();
    } else {
        //获取code码,以获取openid
        $code = $_GET['code'];
        $orderId = $_GET['orderId'];
        $paymentId= $_GET['paymentId'];
        $totalFee = $_GET['totalFee'];
        $openid = $this->getOpenidFromMp($code);
        return $arr;
    }
}
金额的问题

传递的金额,微信是以分为单位,而一般是以元为单位,所以记得金额转换一下,不要传给微信小数点
还有这个金额最好在jsapi这个页面,查询一下数据库该笔订单应支付多少,而不是从提交订单页面传输过来,这样不安全,也别以为请求接口会安全,都不安全,只要存在网络传输就会出现篡改,最好是自己在本页面查询出来,然后传给微信

out_trade_no的问题
(说明这是针对我们的流程设计出次下策)

我们的提交订单会生成一个orderId和paymentId存在我们的两个表中(这两个表),然后因为要给微信传我们自己的订单id,一般来说只需要传orderId就行,但是接收到微信异步通知的支付成功以后, 我们需要根据paymenId去改我们的支付状态。
问题是从微信返回时我们没有paymentId。
用orderId去查可能会不唯一(我们数据库设计如此,会有多订单支付),另一方面是多次请求会慢,所以我采取了把订单id和paymentId拼接在一起作为传给微信,然后支付成功以后微信返回out_trade_no就是我们的orderId和paymentId

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

推荐阅读更多精彩内容

  • 引言 秋高气爽,天气转凉,正是学习工作做的好时候。(~ ̄▽ ̄)~~(~ ̄▽ ̄)~ 我是个phper最近在写微信支付...
    恩就是这个名阅读 7,933评论 2 15
  • 该文仅对于中间这种支付方式有参考价值哟 一、开发背景 在微信公众号中,需要进行微信支付且为微信公众号网页支付。 二...
    英文名叫夏天阅读 1,786评论 0 7
  • 微信公众号支付分为三类 以下主要介绍公众号H5授权支付 首先看一下微信支付的业务逻辑 大部分微信支付逻辑在于服务器...
    撩人C小罗阅读 2,647评论 0 0
  • 燕归巢 作者:天义 陈风怒号兮木叶下, 继往云烟兮悲余茫。 红豆植南国兮别离怅, 侬浓余怀兮顾心凉。 要比邻星...
    天义小哥哥阅读 265评论 0 0
  • 昨天2017年4月29日、周六,我兴致勃勃地参加了世博公园的草莓音乐节,有幸听到了万能青年旅店现场版的《杀死那个石...
    同叔练级之路阅读 608评论 0 49