PHP微信公众号JSAPI网页支付

一、使用场景以及说明

     使用场景:商户已有H5商城网站,用户通过消息或扫描二维码在微信内打开网页时,可以调用微信支付完成下单购买的流程。

 说明:1.用户打开图文消息或者扫描二维码,在微信内置浏览器打开网页进行的支付。

     2.商户网页前端通过使用微信提供的 JSAPI,调用微信支付模块。这种方式,适合需要在商户网页进行选购下单的购买流程。

二、准备工作

公共号支付需要提前在微信公共平台进行业务配置,包括设置支付授权目录设置JS接口安全域名以及设置授权回调页面域名

1.进行微信公众支付之前,我们需要申请个公众号,以及申请微信支付的功能。

2.支付授权目录:

位置:微信支付——>开发配置——>公共号支付

1) 所有使用公众号支付方式发起支付请求的链接地址,都必须在支付授权目录之下;

2) 正式支付授权目录最多设置3个,测试授权目录最多设置1个,且域名必须通过ICP备案;

3) 头部要包含http或https,须细化到二级或三级目录,以左斜杠“/”结尾。

业务中发起支付的页面地址必须在授权目录下,否则调用下单接口时会提示“当前页面的URL未注册”。

 

 

这里再啰嗦地补充两点:

1)不使用框架的情况下,比如将官方sdk下载下来,改成demo放在根目录下,demo/example/jaspi.php,授权目录可参考 http://www. ×××.com/demo/example/

2 )使用框架的情况下,比如ThinkPHP, 项目名叫test ,目录结构如下,test/Application/Home/controller/WxpayController.class.php,授权目录可参考 http://www. ×××.com/index.php/Home/Wxpay/

3.JS接口安全域名:

位置:微信支付——>公共号设置——>功能设置——>JS接口安全域名

说明:设置JS接口安全域名后,公众号开发者可在该域名下调用微信开放的JS接口。

注意事项:

1) 可填写三个域名,要求是一级或一级以上域名(例:qq.com,或者 www.qq.com ),需使用字母、数字及“-”的组合,不支持IP地址及端口号;

2) 填写的域名须通过ICP备案的验证;

3)一个自然月内最多可修改并保存三次。

 

4.授权回调页面域名:

位置:微信支付——>接口权限——>网页授权获取用户基本信息

用户在网页授权页同意授权给公众号后,微信会将授权数据传给一个回调页面,回调页面需在此域名下,以确保安全可靠。

注意事项:

1) 回调页面域名需使用字母、数字及“-”的组合,不支持IP地址及端口号。填写的域名需与实际回调URL中的域名相同;

2) 填写的域名须通过ICP备案的验证。

获取用户授权时redirect_uri对应的URL必须在此域名下,否则回调的地址会无法打开。

 

 

 

三、开发步骤

说明:整个微信公众号支付的流程如下:

【1】用户点击公众号内微信商城打开H5的支付页面

【2】H5页面通过JS调用微信支付接口

【3】微信服务器通过判断输入的JSON数据,返回给客户端相应的成功或失败信息

官方demo结构如下:

 

1.JSAPI支付——H5网页端调起支付接口 

1)用户同意授权,获取code

请求链接:https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#we-chat_redirect

 

 

2)如果有code,直接就通过code能获取用户openid

如果没有code,通过createOauthUrlForCode方法,传入必要参数,获取code

3)如果用户同意授权,页面将跳转至 redirect_uri/?code=CODE&state=STATE。

若用户禁止授权,则重定向后不会带上code参数,仅会带上state参数

redirect_uri?state=STATE

4)code说明以及scope的两种方式说明

【1】code作为换取access_token的票据,每次用户授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。

这里获取到code 和 state(订单号) 后, 对订单进行验证,用户余额也进行验证,如果条件都满足则才能进行下面的流程。

【2】scope的两种方式说明:

微信提供了两种授权方式:snsapi_base和snsapi_userinfo。

snsapi_base:不弹出授权页面,直接跳转,只能获取用户openid;

snsapi_userinfo:弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息。

想要获取code,需要构造如下地址:

2. 通过code换取网页授权access_token (这里也获取到了openid)

1)请求链接:https://api.weixin.qq.com/sns/oauth2/access_token

?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

 

 

2)获取openid:openid是微信支付jsapi支付接口必须的参数

 

 

【1】如果网页授权的作用域为snsapi_base(静默授权),则本步骤中获取到网页授权access_token的同时,也获取到了openid,snsapi_base式的网页授权流程即到此为止。

【2】如果网页授权作用域为snsapi_userinfo,则此时开发者可以通过access_token和openid拉取用户信息了。

 

 

3)getOpenid方法中调用createOauthUrlForOpenid方法获取openid

3. 调用统一支付接口获取预付款id

官方文档说明:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1

1) 设置必传参数,按照签名参数产生签名, 此时参与签名字段有 : appid, mch_id , nonce_str, openid, body , out_trade_no, total_fee, notify_url , trade_type

2) 将必传参数转成xml,createXml方法。

3) 使用xml,请求统一支付接口 https://api.mch.weixin.qq.com/pay/unifiedorder

4) 得到xml格式的返回值结果

5) 将结果转成数组,获取预支付id

4. 使用jsapi调起支付

1. 通过getParameters方法设置必传参数,接口输入数据为json

2. 根据官方demo中jsapi.php 调起支付,得到支付结果

 

注:使用以上方式判断前端返回,res.err_msg 将在用户支付成功后返回 ok,但不保证它绝对可靠。

5. 通用通知接口 callback

1. 支付完成后,微信会把相关支付和用户信息发送到该 URL,商户需要接收处理信息

2. 此时返回的参与签名的参数有 : appid , mch_id, nonce_str, result_code ,openid , is_subscribe,trade_type , bank_type , total_fee , cash_fee , transaction_id,

out_trade_no , time_end

3. 返回数据为xml,相关案例参考demo notify.php

4. 参考 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7

四、可能遇到的问题

1. 返回参数是xml,而不是直接输出success ,这个与微信app支付有区别。

2. 参与签名字段要保证一致,保证前后签名一样。

3. 由于存在重新发送后台通知的情况,因此同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。比如在支付成功后写入了支付日志,那么第二次回调前前判断是否已经有了支付日志,如果有,直接退出不作处理就好了。

4 . 当用户有余额,先用余额支付,剩下的才是微信 , 支付成功时,回调扣去余额

5. 如果同一笔订单号修改多次价格进行支付,第二次支付时,会出现生成预支付订单失败,主要原因是同一笔订单支付时,支付金额不能一样,或者给到第三方的订单号不一样,所以解决方法就是订单号+标志位(比如Z或A)+ 时间戳生成给到的第三方的支付号即可。

===============================支付完成的处理之分割线========================

微信内H5调起支付后,JS API的返回结果get_brand_wcpay_request:ok仅在用户成功完成支付时返回。由于前端交互复杂,get_brand_wcpay_request:cancel或者get_brand_wcpay_request:fail可以统一处理为用户遇到错误或者主动放弃,不必细化区分。
示例代码如下:

 

 

这一篇重点讲支付后,异步接受回调通知,以及处理后同步通知微信服务器。

首先梳理下整个jsapi支付的流程

1.网页授权获取用户openid

2.使用统一下单支付接口,生成JSAPI页面调用的支付参数并签名。

3.使用JSAPI调起支付

4.支付后回调(包括接受异步通知以及做出同步处理)

下面讲一些实际中踩到的坑,注意啦

1.官方demo中的一些需要修改的地方

1)打开lib文件夹下的WxPay.Api.PHP文件,在537行有一段curl网络请求配置代码:

 

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);//非严格校验

 

另外说明下:

在46-49行有一段代码:


//异步通知url未设置,则使用配置文件中的url
if(!$inputObj->IsNotify_urlSet()){
$inputObj->SetNotify_url(WxPayConfig::NOTIFY_URL);//异步通知url

对应的做法是在WxPay.Config.php 中加上这个配置,根据实际回调地址填写
例如:const NOTIFY_URL="http://paysdk.weixin.qq.com/example/notify.php";

 

2)打开lib文件夹下的WxPay.Notify.php文件,第79行的代码:
if($needSign == true && 
$this->GetReturn_code($return_code) == "SUCCESS") 

$this->SetSign(); 
}

替换成

if($needSign == true && 
$this->GetReturn_code() == "SUCCESS") 

$this->SetSign(); 
}

 

3)打开lib文件夹下的WxPay.JsApi.php文件,在99行有一段curl网络请求配置代码:


curl_setopt($ch, CURLOPT_TIMEOUT, $this->curl_timeout);

替换成

curl_setopt($ch, CURLOPT_TIMEOUT, 30);  //此处可根据实际情况填写,单位(秒)

 

2.支付回调验证链接,必须是没有权限验证的

如果你填写的回调链接,还需要登录注册验证的,就不要尝试了,必须要可以无障碍访问的链接,而且也不要有一连串的参数传递。
最好就是简单粗暴的[http://serverName/xxx.php]

以ThinkPHP举例,比如在根目录下,类似于index.php,重新写了一个专门的供支付回调的入口文件payment.php,

和它对应的Application/目录下的模块(WexinApi)、控制器(WeixinPay)及方法(notify)

如图:payment.php

 

纠正一下,上图最后引入ThinkPHP入口文件那句应该改成: require ‘./ThinkPHP/ThinkPHP.php’

 

现在访问[http://serverName/payment.php],就会直接进入到[http://serverName/payment.php/WexinApi/WeixinPay/notify],


这样回调验证链接可以写[http://serverName/payment.php],也可以写[http://serverName/payment.php/WexinApi/WeixinPay/notify]。

 

支付完成,就会进入到之前写好的链接对应的方法

说明:下面那个$data截图不完整,应该是$data=json_decode(json_encode(simplexml_load_string($xml,'SimpleXMLElement',LIBXML_NOCDATA)),true);

 这里有两个地方要注意:

1)升级完PHP7 发现微信支付回调失败。原来是 $GLOBALS['HTTP_RAW_POST_DATA']没有定义的问题。php7 移除了这个全局变量。

像下面这样写就要严谨一点:

     $xml = $GLOBALS['HTTP_RAW_POST_DATA'];//这里在php7下不能获取数据,使用 php://input 代替  

        if(!$xml){  

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

      } 

或者直接写$xml=file_get_contents("php://input");

2)此处file_put_contents(日志文件路径,$xml,FILE_APPEND),linux下,这个地方最好写全局路径:/data/wwwroot/...

为了安全起见,对返回过来的签名,要重新验证:

 

 

 

转载:https://www.cnblogs.com/hld123/p/6626323.html

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值