我的编程空间,编程开发者的网络收藏夹
学习永远不晚

〔支付接入〕微信的 h5 支付和 jsapi 支付

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

〔支付接入〕微信的 h5 支付和 jsapi 支付

🎈 申请商户号

  • 申请地址: https://pay.weixin.qq.com/
  • 如果你还没有微信商户号,请点击上面的链接进行申请,如果已经有了,可以跳过这一步

🎈 申请商户证书

申请API证书

🎈 设置APIv3密钥

  • 首先点击 账户中心API安全设置APIv3密钥设置
  • 会看到有两个密钥,分别是 APIv2密钥APIv3密钥,由于 APIv2密钥 已经逐渐废弃了,所以只需要申请 APIv3密钥 即可
  • 密钥可由数字大小写字母组合,输入任意的 32 位字符,该密钥需要保存好,供后面使用

申请APIv3密钥
设置APIv3密钥

// 生成32位的APIv3随机密钥$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';echo substr(str_shuffle($chars), 0, $length);

🎈 下载 SDK 开发包

# 初始化文件夹composer init# 推荐使用 PHP 包管理工具 Composer 安装 SDKcomposer require wechatpay/wechatpay

🎈 下载平台证书

  • 平台证书跟上面申请的商户证书不是同一个东西,在后期请求中,平台证书和商户证书都要带上
  • 上面命令执行完之后,会有一个 vendor/bin/CertificateDownloader.php 文件
  • 如果你是第一次申请平台证书,需要执行命令:php CertificateDownloader.php -k ${apiV3key} -m ${mchId} -f ${mchPrivateKeyFilePath} -s ${mchSerialNo} -o ${outputFilePath}
  • -k: apiv3 秘钥,上面自己设置的32位数的密钥
  • -m: 商户号,微信商户平台可以查询
  • -f: 微信商户API私钥文件目录,也就是第二步申请商户证书里面生成的 apiclient_key.pem 路径
  • -s: 证书序列号,在 账户中心API安全管理证书 中可以看见,如果有多个证书,找到自己正在使用的证书序列号
  • -o: 生成后的证书保存地址
cd vendor/bin/php CertificateDownloader.php -k 241xxxxxxxxxxxxxxxxx44 -m 1xxxxxxx1 -f ../../cert/merchant/apiclient_key.pem -s Wxxxxxxxxxxxxxxxx4 -o  ../../cert/wechatpay/

获取证书序列号

🎈 关联 AppID 账号

  • 因为使用的是微信支付,所以用户支付后,需要通过微信号通知用户支付的一些信息,所以需要在商户号下至少关联一个公众号

关联公众号

🎈 开通 H5 支付

  • 点击 产品中心我的产品H5支付点击开通
  • 开通后,选择 开发配置H5支付域名 申请添加 H5支付域名
  • 申请支付域名需要先做好产品的页面,申请的时候需要有页面的截图,截图中还要 截取到域名,支付的审核算是很严格的,如果申请不过,驳回后再申请,审核通过的时间会越来越长,所以最好一次性就把材料收集好,另外还要域名的备案的 IPC 截图
  • IPC 备案查询地址: https://beian.miit.gov.cn/
  • 关于域名的填写,如果只填写域名不填写具体域名路径,微信在支付的时候就只会校验域名,这也是最方便的,因为域名下有多个项目有支付功能的话,就不需要重复添加了

开通 H5 支付

申请支付域名

🎈 H5支付流程

  • H5支付是在微信以外的浏览器使用的,如果是微信内的话,使用的是 jsapi 支付
  • 所以一般用户进入页面的第一件事,就是检测用户使用的环境是微信浏览器还是其他浏览器
  • 前端传一些用户挑选商品后的参数,并请求后端处理接口,后端应该将一些参数进行入库,顺便请求 H5 支付接口
  • 接口应该返回跳转链接 h5_url,如果你想用户付款之后到结果页面,需要添加 redirect_url 参数,这个参数一定要用 encodeURIComponent 进行处理
  • 由于官方在 jssapi 支付中说明,不要相信前端的 success 结果,所以需要在结果页中,让用户自动触发查询结果,因此需要返回后端生成的订单号,用作在结果页的用户手动点击查询
// 判断是否微信浏览器function isWeChat() {    var ua = navigator.userAgent.toLowerCase();    if (ua.match(/MicroMessenger/i) == 'micromessenger') {        return true;    } else {        return false;    }}if(isWeChat()) {    // 是微信中打开的产品页面    alert('微信内不支持h5支付,请在外部浏览器打开页面');} else {    // 非微信内打开的产品页面,请求接口,获取支付的跳转链接    // 前端用户选的产品,以及产品的金额,传一些参数过去    let params = {        total: 2, // 单位:元        description: 'Image形象店-深圳腾大-QQ公仔' // 产品的介绍        // ....更多入库参数    };        $.getJSON('后端接口地址/h5?' + $.param(params) + '&callback=?', function(res) {        // 拉起微信支付界面,成功后会跳转到redirect_url链接        $(location).attr("href", res.data.h5_url + "&redirect_url=" + encodeURIComponent(`https://xxxxxx/finish?out_trade_no=${res.data.out_trade_no}`))    });}
only(['name', 'total', 'description', 'phone']);// 生成商户订单号$out_trade_no = getOutTradeNo();// 处理金额// 由于微信使用的是分作为单位,所以前端传的是元的话,需要转换一下$total = $input['total'] * 100;// 商户号$merchantId = '1xxxxxx1';// 从本地文件中加载「商户API私钥」,「商户API私钥」会用来生成请求的签名$merchantPrivateKeyFilePath = 'file://../cert/merchant/apiclient_key.pem';$merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);// 「商户API证书」的「证书序列号」$merchantCertificateSerial = '1xxxxxxxxxxxxxxxxxxxxx91';// 从本地文件中加载「微信支付平台证书」,用来验证微信支付应答的签名$platformCertificateFilePath = 'file://../cert/wechatpay/wechatpay_4xxxxxxxxxxxxxxxxxxx9.pem';$platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);// 从「微信支付平台证书」中获取「证书序列号」$platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);// 构造一个 APIv3 客户端实例$instance = Builder::factory([    'mchid'      => $merchantId,    'serial'     => $merchantCertificateSerial,    'privateKey' => $merchantPrivateKeyInstance,    'certs'      => [        $platformCertificateSerial => $platformPublicKeyInstance,    ],]);try {    $resp = $instance        ->chain('v3/pay/transactions/h5')        ->post(['json' => [            'mchid'        => $merchantId, // 商户号            'out_trade_no' => $out_trade_no, // 商户订单号            'appid'        => '********换成跟商户号绑定的公众号APPID**********',            'description'  => $input['description'], //商品描述            'notify_url'   => 'https://xxxxx/notify', // 用户支付后的回调地址,在这里修改订单的状态            'amount'       => [                'total'    => $total, // 微信处理的单位是分                'currency' => 'CNY'            ],            'scene_info' => [                'payer_client_ip' => getClientIP(), // 有些框架有自带获取获取客户端IP                'h5_info' => [                    'type' => 'Wap'                ]            ]        ]]);                   // 如果请求成功,需要将一些参数进行入库,这里仅作演示,非正式数据入库   $response = Db::table('order')->insert([       'name' => $input['name'],       'description' => $input['description'],       'total' => $input['total'],       'phone' => $input['phone'],       'trade_state' => 'START',   ]);      // 入库成功后,将跳转链接和订单号传给前端,前端拿到跳转地址跳转即可   if($response) {       return jsonp([        'code' => 200,        'msg' => '操作成功',        'data' => [              'out_trade_no' => $out_trade_no,              'h5_url' => json_decode($resp->getBody(), true)['h5_url']           ]        ]);   } else {       return jsonp([        'code' => 100,        'msg' => '操作失败'       ]);   }} catch (\Exception $e) {    // 进行错误处理    if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {        $r = $e->getResponse();        echo $r->getBody(), PHP_EOL, PHP_EOL, PHP_EOL;    }}// 生成唯一商户订单号,订单号不能超过32位,并且在同一个商户下订单号不能重复// 如果并发不高,基本这样生成就可以,不会有重复的情况出现的function getOutTradeNo(){    $out_trade_no = date('ymdHis') . mt_rand(1000, 9999) . uniqid();    return mb_substr($out_trade_no, 0, 32);}// 获取客户端的IPfunction getClientIP(){    if (@$_SERVER["HTTP_ALI_CDN_REAL_IP"]) {        $ip = $_SERVER["HTTP_ALI_CDN_REAL_IP"];    } elseif (@$_SERVER["HTTP_X_FORWARDED_FOR"] ?: false) {        $ips = explode(',', $_SERVER["HTTP_X_FORWARDED_FOR"]);        $ip = $ips[0];    } elseif (@$_SERVER["HTTP_CDN_class="lazy" data-src_IP"] ?: false) {        $ip = $_SERVER["HTTP_CDN_class="lazy" data-src_IP"];    } elseif (getenv('HTTP_CLIENT_IP')) {        $ip = getenv('HTTP_CLIENT_IP');    } elseif (getenv('HTTP_X_FORWARDED')) {        $ip = getenv('HTTP_X_FORWARDED');    } elseif (getenv('HTTP_FORWARDED_FOR')) {        $ip = getenv('HTTP_FORWARDED_FOR');    } elseif (getenv('HTTP_FORWARDED')) {        $ip = getenv('HTTP_FORWARDED');    } else {        $ip = $_SERVER['REMOTE_ADDR'];    }    $ip = str_replace(['::ffff:', '[', ']'], ['', '', ''], $ip);    return $ip;}
    // 获取参数    $inBody = file_get_contents('php://input');    // APIv3密钥    $apiv3Key = 'xxxxxxxxxxxx';    // 转换通知的JSON文本消息为PHP Array数组    $inBodyArray = (array)json_decode($inBody, true);        // 加密文本消息解密    $inBodyResource = AesGcm::decrypt(        $inBodyArray['resource']['ciphertext'],        $apiv3Key,        $inBodyArray['resource']['nonce'],        $inBodyArray['resource']['associated_data']    );    // 把解密后的文本转换为PHP Array数组    $inBodyResourceArray = (array)json_decode($inBodyResource, true);    try {        // 获取订单信息        $order = Db::table('order')->where('out_trade_no', $inBodyResourceArray['out_trade_no'])->first();        Db::startTrans();        if ($order) {            // 修改order订单的状态            Db::table('order')->where('id', $order['id'])->update([                'openid' => $inBodyResourceArray['payer']['openid'],                'trade_state' => $inBodyResourceArray['trade_state']            ]);        Db::table('payment')->insert([                 'out_trade_no' => $inBodyResourceArray['out_trade_no'],                'transaction_id' => $inBodyResourceArray['transaction_id'],                'trade_type' => $inBodyResourceArray['trade_type'],                'trade_state' => $inBodyResourceArray['trade_state'],                'trade_state_desc' => $inBodyResourceArray['trade_state_desc'],                'total_amount' => $inBodyResourceArray['amount']['total'],                'bank_type' => $inBodyResourceArray['bank_type'],                'success_time' => strtotime($inBodyResourceArray['success_time'])            ]);            Db::commit();        } else {            Db::rollback();        }    } catch (\Exception $e) {        Db::rollback();    }}

🎈 开通 JSAPI 支付

  • 点击 产品中心我的产品JSAPI支付点击开通
  • 开通后,选择 开发配置JSAPI支付域名 申请添加 JSAPI支付域名
  • 关于申请支付域名的流程基本都差不多要求也差不多,看上面的 H5支付域名 申请就行,这里就不过多赘述了

开通 JSAPI 支付

🎈 JSAPI 支付流程

  • JSAPI支付是在微信内的浏览器使用的,如果用户是在微信外打开的话,需要提醒去微信内打开页面
  • JSAPI支付需要使用微信内置的 WeixinJSBridge.invoke 方法
  • 由于 JSAPI 调用支付需要用到用户的 openid,所以需要想方设法在用户调用 JSAPI 之前获取到 openid点击查看获取 openid 的官方文档
  • 获取用户 openid,需要先获取 code,这个经常做微信业务的人都知道,那么如何在用户无感知的情况下就获取到 openid
  • 思路就是,一般支付最少会有3个页面,这里标注为abc 三个页面,通常是在 a 页面挑选商品,在 b页面确认商品,也就是付款页面,c 页面查询支付状态
  • 由于 code 的存在时间只有5分钟,所以注定 code 获得后不能长时间不使用,也就是说用户一旦在某个页面超过5分钟,这个 code 就失效了,因此最好的方法就是获取 code 后,立马获取 openid
  • 那么就应该设计成从a 页面先跳转到获取 code 页面再跳转到 b 页面,而在 b 页面的一开始就去请求接口,获取用户的 openid 即可
  • 跳转到 b 页面后,链接后自动带上 code参数,链接应该是 https://xxxx/b.html?code=xxxxxxxx
// a页面,仅做逻辑演示,更加具体的逻辑需要自己完善// 判断是否微信浏览器function isWeChat() {    var ua = navigator.userAgent.toLowerCase();    if (ua.match(/MicroMessenger/i) == 'micromessenger') {        return true;    } else {        return false;    }}if(!isWeChat()) {    // 非微信内打开的产品页面    alert('微信外不支持JSAPI支付,请在微信中打开页面');    return false;}// 用户挑选完商品后跳转,这里appid需要上面跟商户绑定的公众号appid// 微信授权分为静默授权和非静默授权,其中非静默授权,需要用户点击确认授权后,才可以获取code,// 因为这里主打一个用户无感知,而且我们只需要openid即可,所以我们只需要使用静默授权即可// 静默授权可以获取用户更多的信息,比如头像、昵称等,而静默授权只能获取openid,这点需要注意,具体情况选择不同// 非静默授权// $(location).attr('href', `https://open.weixin.qq.com/connect/oauth2/authorize?appid=xxxxxxxxxxx&redirect_uri=${encodeURIComponent('https://xxxx/b.html')}&response_type=code&scope=snsapi_userinfo#wechat_redirect`)// 静默授权$(location).attr('href', `https://open.weixin.qq.com/connect/oauth2/authorize?appid=xxxxxxxxxxx&redirect_uri=${encodeURIComponent('https://xxxx/b.html')}&response_type=code&scope=snsapi_base#wechat_redirect`)
// b页面,仅做逻辑演示,更加具体的逻辑需要自己完善let openid = '';// 获取code, 请求接口获取openidfunction getParamUrl(name, url) {  if (!url) url = location.href;  if (url.indexOf('?') == -1) return '';  try {    var re = new RegExp("" + name + "=([^&?]*)", "ig");    return ((url.match(re)) ? (decodeURIComponent(url.match(re)[0].substr(name.length + 1))) : '');  } catch (_e) {    return '';  }}let code = getParamUrl('code');$.getJSON('后端接口地址/openid?callback=?', function(res) {    if(res.code == 200) {        openid = res.data;    } else {        console.error(res.msg);    }})// 用户确定订单后,拉起支付let params = {    total: 2, // 单位:元    description: 'Image形象店-深圳腾大-QQ公仔', // 产品的介绍    openid: openid //用户的openid    // ....更多入库参数};$.getJSON('后端接口地址/jssapi?' + $.param(params) + '&callback=?', function(res) {    WeixinJSBridge.invoke('getBrandWCPayRequest', {      'appId': res.data.sign.appId,      'timeStamp': res.data.sign.timeStamp,      'nonceStr': res.data.sign.nonceStr,      'package': res.data.sign.package,      'signType': res.data.sign.signType,      'paySign': res.data.sign.paySign    }, function (response) {      if (response.err_msg == "get_brand_wcpay_request:ok") {        $(location).attr("href", `https://xxxxxx/finish?out_trade_no=${res.data.out_trade_no}`)      } else {        // 有些用户调起了支付,但是未付款取消的处理方式,你可以给他简单简单提示        toast('支付异常取消')        // 当然有些用户是误操作,你可以提醒二次支付        if(confirm('检测到你操作有误,是否重新支付?')) {            WeixinJSBridge.invoke('getBrandWCPayRequest', {                  'appId': res.data.sign.appId,                  'timeStamp': res.data.sign.timeStamp,                  'nonceStr': res.data.sign.nonceStr,                  'package': res.data.sign.package,                  'signType': res.data.sign.signType,                  'paySign': res.data.sign.paySign                }, function (response) {                if (response.err_msg == "get_brand_wcpay_request:ok") {                    $(location).attr("href", `https://xxxxxx/finish?out_trade_no=${res.data.out_trade_no}`)                }            })        }      }    });});
only(['code']);$response = getCurl("https://api.weixin.qq.com/sns/oauth2/access_token?appid={$this->appid}&secret={$this->secret}&code={$input['code']}&grant_type=authorization_code");$openid = json_decode($response, true)['openid'];// 返回openidreturn jsonp([    'code' => 200,    'msg' => '获取成功',    'data' => $openid]);// 封装的GET请求function getCurl($url, $timeout = 5){    $ch = curl_init();    curl_setopt($ch, CURLOPT_URL, $url);    curl_setopt($ch, CURLOPT_HEADER, 0);    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);    $result = curl_exec($ch);    curl_close($ch);    return $result;}
only(['openid', 'name', 'total', 'description', 'phone']);// 生成商户订单号$out_trade_no = getOutTradeNo();// 处理金额// 由于微信使用的是分作为单位,所以前端传的是元的话,需要转换一下$total = $input['total'] * 100;// 商户号$merchantId = '1xxxxxx1';// 从本地文件中加载「商户API私钥」,「商户API私钥」会用来生成请求的签名$merchantPrivateKeyFilePath = 'file://../cert/merchant/apiclient_key.pem';$merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);// 「商户API证书」的「证书序列号」$merchantCertificateSerial = '1xxxxxxxxxxxxxxxxxxxxx91';// 从本地文件中加载「微信支付平台证书」,用来验证微信支付应答的签名$platformCertificateFilePath = 'file://../cert/wechatpay/wechatpay_4xxxxxxxxxxxxxxxxxxx9.pem';$platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);// 从「微信支付平台证书」中获取「证书序列号」$platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);// 构造一个 APIv3 客户端实例$instance = Builder::factory([    'mchid'      => $merchantId,    'serial'     => $merchantCertificateSerial,    'privateKey' => $merchantPrivateKeyInstance,    'certs'      => [        $platformCertificateSerial => $platformPublicKeyInstance,    ],]);try {    // 调用 transactions/jsapi 接口后会生成prepay_id    $resp = $this->instance()        ->chain('v3/pay/transactions/jsapi')        ->post(['json' => [            'mchid'        => $merchantId, // 商户号            'out_trade_no' => $out_trade_no, // 商户订单号            'appid'        => '********换成跟商户号绑定的公众号APPID**********',            'description'  => $input['description'], //商品描述            'notify_url'   => 'https://xxxxx/notify', // 用户支付后的回调地址,在这里修改订单的状态            'amount' => [                'total' => $total,                'currency' => 'CNY'            ],            'payer' => [                'openid' => $input['openid']            ]        ]]);            // 需要根据prepay_id去生成加密的信息    $prepay_id = json_decode($resp->getBody(), true)['prepay_id'];    $sign = getSign($prepay_id);           // 如果请求成功,需要将一些参数进行入库,这里仅作演示,非正式数据入库   $response = Db::table('order')->insert([       'openid' => $input['openid'],       'name' => $input['name'],       'description' => $input['description'],       'total' => $input['total'],       'phone' => $input['phone'],       'trade_state' => 'START',   ]);      // 入库成功后,将跳转链接和订单号传给前端,前端拿到跳转地址跳转即可   if($response) {       return jsonp([        'code' => 200,        'msg' => '操作成功',        'data' => [              'out_trade_no' => $out_trade_no,              'sign' => $sign           ]        ]);   } else {       return jsonp([        'code' => 100,        'msg' => '操作失败'       ]);   }} catch (\Exception $e) {    // 进行错误处理    if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {        $r = $e->getResponse();        echo $r->getBody(), PHP_EOL, PHP_EOL, PHP_EOL;    }}// 获取加密参数function getSign($prepay_id){    $merchantPrivateKeyInstance = Rsa::from($this->merchantPrivateKeyFilePath);    $params = [        'appId' => $this->appid,        'timeStamp' => (string)Formatter::timestamp(),        'nonceStr' => Formatter::nonce(),        'package' => 'prepay_id=' . $prepay_id,    ];    $params += ['paySign' => Rsa::sign(        Formatter::joinedByLineFeed(...array_values($params)),        $merchantPrivateKeyInstance    ), 'signType' => 'RSA'];    return $params;}

🎈 通用微信支付库封装

  • 由于直接使用微信的支付库,代码非常的匀余,所以封装了一个微信支付库
  • 由于只针对一些业务的 api封装,所以肯定不全,需要的可以自己添加需要的api
  • 微信支付API接口列表: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/index.shtml
    // appid    private $appid;    // 商户号    private $merchantId;    // 商户API私钥    private $merchantPrivateKeyFilePath;    // 证书序列号    private $merchantCertificateSerial;    // 微信支付平台证书    private $platformCertificateFilePath;        public function __construct($appid = '', $merchantId = '', $merchantCertificateSerial = '')    {        $this->appid = $appid ?: '换成自己的APPID';        $this->merchantId = $merchantId ?: '换成自己的商户号';        $this->merchantCertificateSerial = $merchantCertificateSerial ?: '换成自己的证书序列号';        $this->merchantPrivateKeyFilePath = 'file:///common/cert/merchant/apiclient_key.pem'; // 换成自己的        $this->platformCertificateFilePath = 'file:///common/cert/wechatpay/wechatpay_xxx.pem'; // 换成自己的    }        protected function instance()    {        $merchantPrivateKeyInstance = Rsa::from($this->merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);        $platformPublicKeyInstance = Rsa::from($this->platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);        $platformCertificateSerial = PemUtil::parseCertificateSerialNo($this->platformCertificateFilePath);        $instance = Builder::factory([            'mchid' => $this->merchantId,            'serial' => $this->merchantCertificateSerial,            'privateKey' => $merchantPrivateKeyInstance,            'certs' => [                $platformCertificateSerial => $platformPublicKeyInstance,            ],        ]);        return $instance;    }    public function getSign($prepay_id)    {        $merchantPrivateKeyInstance = Rsa::from($this->merchantPrivateKeyFilePath);        $params = [            'appId' => $this->appid,            'timeStamp' => (string)Formatter::timestamp(),            'nonceStr' => Formatter::nonce(),            'package' => 'prepay_id=' . $prepay_id,        ];        $params += ['paySign' => Rsa::sign(            Formatter::joinedByLineFeed(...array_values($params)),            $merchantPrivateKeyInstance        ), 'signType' => 'RSA'];        return $params;    }    public function checkOutTradeNo($out_trade_no)    {        try {            $resp = $this->instance()                ->v3->pay->transactions->outTradeNo->_out_trade_no_                ->get([                    // Query 参数                    'query' => ['mchid' => $this->merchantId],                    // 变量名 => 变量值                    'out_trade_no' => $out_trade_no,                ]);            return $resp->getBody();        } catch (\Exception $e) {            // 进行错误处理            echo $e->getMessage(), PHP_EOL;            if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {                $r = $e->getResponse();                echo $r->getStatusCode() . ' ' . $r->getReasonPhrase(), PHP_EOL;                echo $r->getBody(), PHP_EOL, PHP_EOL, PHP_EOL;            }            echo $e->getTraceAsString(), PHP_EOL;        }    }        // h5下单    public function h5($total, $out_trade_no, $description, $notify_url)    {        try {            $resp = $this->instance()                ->chain('v3/pay/transactions/h5')                ->post(['json' => [                    'mchid' => $this->merchantId,                    'out_trade_no' => $out_trade_no,                    'appid' => $this->appid,                    'description' => $description,                    'notify_url' => $notify_url,                    'amount' => [                        'total' => $total,                        'currency' => 'CNY'                    ],                    'scene_info' => [                        'payer_client_ip' => Helper::getClientIp(),                        'h5_info' => ['type' => 'Wap'                        ]                    ]                ]]);            return $resp->getBody();        } catch (\Exception $e) {            // 进行错误处理            echo $e->getMessage(), PHP_EOL;            if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {                $r = $e->getResponse();                echo $r->getStatusCode() . ' ' . $r->getReasonPhrase(), PHP_EOL;                echo $r->getBody(), PHP_EOL, PHP_EOL, PHP_EOL;            }            echo $e->getTraceAsString(), PHP_EOL;        }    }        // jsapi下单    public function jsapi($openid, $total, $out_trade_no, $description, $notify_url)    {        try {            $resp = $this->instance()                ->chain('v3/pay/transactions/jsapi')                ->post(['json' => [                    'mchid' => $this->merchantId,                    'out_trade_no' => $out_trade_no,                    'appid' => $this->appid,                    'description' => $description,                    'notify_url' => $notify_url,                    'amount' => [                        'total' => $total,                        'currency' => 'CNY'                    ],                    'payer' => [                        'openid' => $openid                    ]                ]]);            return $resp->getBody();        } catch (\Exception $e) {            // 进行错误处理            echo $e->getMessage(), PHP_EOL;            if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {                $r = $e->getResponse();                echo $r->getStatusCode() . ' ' . $r->getReasonPhrase(), PHP_EOL;                echo $r->getBody(), PHP_EOL, PHP_EOL, PHP_EOL;            }            echo $e->getTraceAsString(), PHP_EOL;        }    }           // todo... 更多接口可根据官方文档列表自行添加}
        static public function getClientIP()    {        if (@$_SERVER["HTTP_ALI_CDN_REAL_IP"]) {            $ip = $_SERVER["HTTP_ALI_CDN_REAL_IP"];        } elseif (@$_SERVER["HTTP_X_FORWARDED_FOR"] ?: false) {            $ips = explode(',', $_SERVER["HTTP_X_FORWARDED_FOR"]);            $ip = $ips[0];        } elseif (@$_SERVER["HTTP_CDN_class="lazy" data-src_IP"] ?: false) {            $ip = $_SERVER["HTTP_CDN_class="lazy" data-src_IP"];        } elseif (getenv('HTTP_CLIENT_IP')) {            $ip = getenv('HTTP_CLIENT_IP');        } elseif (getenv('HTTP_X_FORWARDED')) {            $ip = getenv('HTTP_X_FORWARDED');        } elseif (getenv('HTTP_FORWARDED_FOR')) {            $ip = getenv('HTTP_FORWARDED_FOR');        } elseif (getenv('HTTP_FORWARDED')) {            $ip = getenv('HTTP_FORWARDED');        } else {            $ip = $_SERVER['REMOTE_ADDR'];        }        $ip = str_replace(['::ffff:', '[', ']'], ['', '', ''], $ip);        return $ip;    }        static public function createRandomStr($length = 32, $type = 0)    {        switch ($type) {            case 1:                $chars = '0123456789';                break;            case 2:                $chars = 'abcdefghijklmnopqrstuvwxyz';                break;            case 3:                $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';                break;            case 4:                $chars = 'abcdefghijklmnopqrstuvwxyz0123456789';                break;            case 5:                $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';                break;            default:                $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';                break;        }        return substr(str_shuffle($chars), 0, $length);    }        static public function getCurl($url, $timeout = 5)    {        $ch = curl_init();        curl_setopt($ch, CURLOPT_URL, $url);        curl_setopt($ch, CURLOPT_HEADER, 0);        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);        $result = curl_exec($ch);        curl_close($ch);        return $result;    }        static public function postCurl($url, $data, $header = [], $timeout = 5)    {        $ch = curl_init();        curl_setopt($ch, CURLOPT_URL, $url);        if ($header) {            curl_setopt($ch, CURLOPT_HTTPHEADER, $header);        }        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);        curl_setopt($ch, CURLOPT_POST, 1);        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);        $result = curl_exec($ch);        curl_close($ch);        return $result;    }}

来源地址:https://blog.csdn.net/weixin_41635750/article/details/131810739

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

〔支付接入〕微信的 h5 支付和 jsapi 支付

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

〔支付接入〕微信的 h5 支付和 jsapi 支付

✨ 目录 🎈 申请商户号🎈 申请商户证书🎈 设置APIv3密钥🎈 下载 SDK 开发包🎈 下载平台证书🎈 关联 AppID 账
2023-08-16

Android 支付宝支付、微信支付、银联支付 整合第三方支付接入方法(后台订单支付API设计)

客户端获取后台支付API请求参数的设计参数样例:{ data: { method: 1, platform: 1, version:"1.0", relate_orders:"B201602031023,B2016020310231", o
2022-06-06

iOS实现H5支付(微信、支付宝)原生封装

前言支付分APP支付、H5支付、扫码支付等。app支付一般在app中使用,并且需要集成相应的支付SDK,H5支付多用于网页。如果你的APP不想集成支付SDK,又想实现支付功能,你可以在项目中使用H5支付。本文主要讲述如何将H5支付封装成一个
2022-06-05

Android支付宝和微信支付集成

场景 随着移动支付的兴起,在我们的app'中,会经常有集成支付的需求.这时候一般都会采用微信和支付宝的sdk 来集成 (一)支付宝支付 在使用支付宝支付的过程中,我们是在服务器端生成订单,客户端访问接口,并得到订单信息,调用接口支付,支付成
2022-06-06

h5移动端如何调用支付宝、微信支付

这篇文章给大家分享的是有关h5移动端如何调用支付宝、微信支付的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。代码:var aliChannel = null; var wxChannel = null;
2023-06-09

如何进行vue项目中的支付功能实现(微信支付和支付宝支付)

如何进行vue项目中的支付功能实现(微信支付和支付宝支付),针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。项目中常见的支付方式 支付宝支付 微信支付 余额支付(
2023-06-22

Android App支付系列(一):微信支付接入详细指南(附官方支付demo)

写在前面 一家移动互联网公司,说到底,要盈利总是需要付费用户的,自己开发支付系统显然是不明智的,国内已经有多家成熟的移动支付提供商,腾讯就是其中之一。梳理了下微信支付的接入,今天给大家分享下腾讯旗下的微信支付SDK的接入流程。 接入流程 1
2022-06-06

微信支付、支付宝支付等常用第三方支付通道接口手续费对比

微信支付、支付宝等第三方支付,需要和银联、网联对接,有清算机构和银行的交易处理通道成本。费率指支付手续费的费率,不同行业、不同的支付平台、不同的支付额度或次数所对应的通道费率是不一样的。
2023-01-28

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录