使用TP5开发微信服务商支付

1.下载微信官方的库和SDK

  wechatpay-guzzle-middleware

在项目目录中,通过composer命令行添加:

composer require wechatpay/wechatpay-guzzle-middleware

2.开通微信支付服务商平台

特别备注:微信支付API v3要求商户对请求进行签名。微信支付会在收到请求后进行签名的验证。如果签名验证不通过,微信支付API v3将会拒绝处理请求,并返回401 Unauthorized

什么是商户证书?什么是平台证书?

  • “商户证书”是指由商户申请的,包含商户的商户号、公司名称、公钥信息的证书。

  • ”平台证书”是指由微信支付负责申请的,包含微信支付平台标识、公钥信息的证书。

商户在调用 API 时用自身的私钥签名,微信支付使用商户证书中的公钥来验签。微信支付在响应的报文中使用自身的私钥签名,商户使用平台证书中的公钥来验签。

平台证书如何获取?

获取平台公钥、平台序列号
(证书下载工具)

地址:GitHub - wechatpay-apiv3/CertificateDownloader: Java 微信支付 APIv3 平台证书的命令行下载工具

下载文件:CertificateDownloader-1.1.jar

该工具已经通过 Maven 打包成 CertificateDownloader.jar,可在 release  中下载。

在终端执行:

这里,必需参数有:

    商户的私钥文件,即 -f
    证书解密的密钥,即 -k
    商户号,即 -m
    保存证书的路径,即 -o
    商户证书的序列号,即 -s

非必需参数有:

    微信支付证书,用于验签,即 -c

完整命令如:

java -jar CertificateDownloader.jar -k ${apiV3key} -m ${mchId} -f ${mchPrivateKeyFilePath} -s ${mchSerialNo} -o ${outputFilePath} -c ${wechatpayCertificateFilePath}

执行成功之后,获取当前平台证书的路径

① 获取签名

$ echo -n -e \
  "GET\n/v3/certificates\n1554208460\n593BEC0C930BF1AFEB40B4A08C8FB242\n\n" \
  | openssl dgst -sha256 -sign apiclient_key.pem \
  | openssl base64 -A
uOVRnA4qG/MNnYzdQxJanN+zU+lTgIcnU9BxGw5dKjK+VdEUz2FeIoC+D5sB/LN+nGzX3hfZg6r5wT1pl2ZobmIc6p0ldN7J6yDgUzbX8Uk3sD4a4eZVPTBvqNDoUqcYMlZ9uuDdCvNv4TM3c1WzsXUrExwVkI1XO5jCNbgDJ25nkT/c1gIFvqoogl7MdSFGc4W4xZsqCItnqbypR3RuGIlR9h9vlRsy7zJR9PBI83X8alLDIfR1ukt1P7tMnmogZ0cuDY8cZsd8ZlCgLadmvej58SLsIkVxFJ8XyUgx9FmutKSYTmYtWBZ0+tNvfGmbXU7cob8H/4nLBiCwIUFluw==

②最终我们可以组一个包含了签名的HTTP请求了

$ curl https://api.mch.weixin.qq.com/v3/certificates -H 'Authorization: WECHATPAY2-SHA256-RSA2048 mchid="1900009191",nonce_str="593BEC0C930BF1AFEB40B4A08C8FB242",signature="uOVRnA4qG/MNnYzdQxJanN+zU+lTgIcnU9BxGw5dKjK+VdEUz2FeIoC+D5sB/LN+nGzX3hfZg6r5wT1pl2ZobmIc6p0ldN7J6yDgUzbX8Uk3sD4a4eZVPTBvqNDoUqcYMlZ9uuDdCvNv4TM3c1WzsXUrExwVkI1XO5jCNbgDJ25nkT/c1gIFvqoogl7MdSFGc4W4xZsqCItnqbypR3RuGIlR9h9vlRsy7zJR9PBI83X8alLDIfR1ukt1P7tMnmogZ0cuDY8cZsd8ZlCgLadmvej58SLsIkVxFJ8XyUgx9FmutKSYTmYtWBZ0+tNvfGmbXU7cob8H/4nLBiCwIUFluw==",timestamp="1554208460",serial_no="1DDE55AD98ED71D6EDD4A4A16996DE7B47773A8C"'

执行成功之后,得到平台证书序列号:serial_no ,保存起来

以上步骤获取了平台的证书路径和平台证书序列号

以下是实际支付PHP代码:

①.发起微信扫码支付,生成待支付二维码

namespace app\api\controller;

use app\common\controller\Api;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Client;
use WechatPay\GuzzleMiddleware\WechatPayMiddleware;
use WechatPay\GuzzleMiddleware\Util\PemUtil;
use fast\Random;


/**
 * 微信服务商多商户支付接口
 */
class Wechatpay extends Api
{
    protected $noNeedLogin = [];
    protected $noNeedRight = ['*'];


    public function _initialize()
    {
        parent::_initialize();
        // 商户相关配置,
        $this->merchantId= Config('site.merchantId'); // 商户号
        // 商户API证书序列号
        $this->merchantSerialNumber= Config('site.merchantSerialNumber');
        // 商户私钥文件路径
        $this->merchantPrivateKey=PemUtil::loadPrivateKey(".".Config('site.wechat_merchantPrivateKey'));
        // 微信支付平台配置,微信支付平台证书文件路径
        $this->wechatpayCertificate=PemUtil::loadCertificate(".".Config('site.wechatpayCertificate'));

        // 构造一个WechatPayMiddleware
        $wechatpayMiddleware = WechatPayMiddleware::builder()
            ->withMerchant($this->merchantId, $this->merchantSerialNumber, $this->merchantPrivateKey) // 传入商户相关配置
            ->withWechatPay([ $this->wechatpayCertificate ]) // 可传入多个微信支付平台证书,参数类型为array
            ->build();

        // 将WechatPayMiddleware添加到Guzzle的HandlerStack中
        $HandlerStack = new HandlerStack();
        $stack = $HandlerStack::create();
        $stack->push($wechatpayMiddleware, 'wechatpay');

        // 创建Guzzle HTTP Client时,将HandlerStack传入,接下来,正常使用Guzzle发起API请求,WechatPayMiddleware会自动地处理签名和验签
        $client = new Client(['handler' => $stack]);

        $this->client = $client;

    }

    /**
     * @ApiTitle    (微信服务商支付生成二维码)
     * @ApiMethod   (POST)
     * @ApiRoute    (/api/wechatpay/wechat_qrcode)
     * @ApiHeaders  (name=token, type=string, required=true, description="请求的TOKEN")
     * @ApiHeaders  (name=sub_appid, type=string, required=true, description="子商户申请的公众号appid")
     * @ApiHeaders  (name=sub_mchid, type=string, required=true, description="子商户的商户号")
     * @ApiHeaders  (name=description, type=string, required=true, description="商品描述")
     * @ApiHeaders  (name=notify_url, type=string, required=true, description="通知地址")
     * @ApiHeaders  (name=goods_tag, type=string, required=true, description="订单优惠标记 示例值:WXG ")
     * @ApiHeaders  (name=total, type=float, required=true, description="待支付总金额")
     * @ApiSummary  (商户Native下单接口,微信后台系统返回链接参数code_url,商户后台系统将code_url值生成二维码图片,用户使用微信客户端扫码后发起支付。code_url有效期为2小时,过期后扫码不能再发起支付。)
     */
    public function wechat_qrcode()
    {
        $data = $this->request->post();
        try {
            $resp = $this->client->request(
                'POST',
                'https://api.mch.weixin.qq.com/v3/pay/partner/transactions/native', //请求URL
                [
                    // JSON请求体
                    'json' => [
                        "sp_appid" => Config('site.sp_appid'),  // 服务商申请的公众号appid
                        "sp_mchid" => $this->merchantId,             //服务商户号,由微信支付生成并下发
                        "description" => $data['description'],       //商品描述
                        "notify_url" => $data['notify_url'],         //通知地址
                        "out_trade_no" => Random::numeric(16),       //商户订单号
                        "goods_tag" => $data['goods_tag'],           //订单优惠标记 示例值:WXG
                     
                        "sub_appid" => $data['sub_appid'],            //子商户申请的公众号appid
                        "sub_mchid" => $data['sub_mchid'],            //子商户的商户号,由微信支付生成并下发。
                        "amount" => [
                            "total" => $data['total'] * 100,          //以分为单位
                            "currency" => "CNY",
                        ],
                        "settle_info"=>[
                            "profit_sharing" => true                  //是否指定分账,枚举值 true:是,false:否
                        ]
                    ],
                    'headers' => [ 'Accept' => 'application/json' ]
                ]
            );
            $statusCode = $resp->getStatusCode();
            if ($statusCode == 200) { //处理成功
              
                $url = json_decode($resp->getBody()->getContents(),true);
                $code_img = Controller('Common')->qrcode($url['code_url'],'wechat_pay_server');
                $img = urls()."/".$code_img;
                $this->success('success',$img);
            } else if ($statusCode == 204) { //处理成功,无返回Body
                $this->success('success');
            }
        } catch (RequestException $e) {
            // 进行错误处理
            $data = [
                'status'=>  $e->getResponse()->getStatusCode(),
                'message'=> json_decode($e->getResponse()->getBody(),true)
            ];
            $this->error('ERROR',$data);
        }
    }

生成微信pc端扫码支付的二维码

②. 服务商添加分账接收方API

/**
     * @ApiTitle    (服务商添加分账接收方API)
     * @ApiMethod   (POST)
     * @ApiRoute    (/api/wechatpay/receivers)
     * @ApiHeaders  (name=token, type=string, required=true, description="请求的TOKEN")
     * @ApiHeaders  (name=sub_mchid, type=string, required=true, description="子商户的商户号,必填")
     * @ApiHeaders  (name=sub_appid, type=string, required=true, description="子商户的appid")
     * @ApiSummary  (服务商发起添加分账接收方请求,建立分账接收方列表。后续可通过发起分账请求,将分账方商户结算后的资金,分到该分账接收方 )
     */
    public function receivers()
    {
        $data = $this->request->post();
        if($data['sub_mchid']=='')
        {
            $this->error('子商户的商户号不能为空');
        }
        $name = $this->getEncrypt('分账房商户名称');
        try {
            $resp = $this->client->request(
                'POST',
                'https://api.mch.weixin.qq.com/v3/profitsharing/receivers/add', //请求URL
                [
                    // JSON请求体
                    'json' => [
                        "sub_mchid" => $data['sub_mchid'],            //分账出资的电商平台二级商户,填写微信支付分配的商户号。
                        "appid"     => Config('site.sp_appid'),  // 服务商申请的公众号appid
                        "sub_appid" => $data['sub_appid'],             // 微信分配的子商户公众账号
                        //MERCHANT_ID:商户ID
                        //PERSONAL_OPENID:个人
                        //openid(由父商户APPID转换得到)
                        //PERSONAL_SUB_OPENID:个人
                        //sub_openid(由子商户APPID转换得到)
                        "type"      => 'MERCHANT_ID',
                        "name"      => $name,                          //分账接收方类型是MERCHANT_ID时,是商户全称(必传)
                        "account"   => $this->merchantId,              //分账接收方账号
                        "relation_type"   => 'SERVICE_PROVIDER',       //与分账方的关系类型
                        "custom_relation" => 'MEETCONF',               //与分账方的关系类型
                    ],
                    'headers' => [
                        'Accept' => 'application/json',
                        'Wechatpay-Serial'=> Config('site.Wechatpay-Serial')
                    ]
                ]
            );
            $statusCode = $resp->getStatusCode();
            if ($statusCode == 200) { //处理成功
                $data = json_decode($resp->getBody()->getContents(),true);
                $this->success('success',$data);
            } else if ($statusCode == 204) { //处理成功,无返回Body
                $this->success('success');
            }
        } catch (RequestException $e) {
            // 进行错误处理
            $data = [
                'status'=>  $e->getResponse()->getStatusCode(),
                'message'=> json_decode($e->getResponse()->getBody(),true)
            ];
            $this->error('ERROR',$data);
        }
    }

③.服务商发起请求分账API

/**
     * @ApiTitle    (服务商发起请求分账API)
     * @ApiMethod   (POST)
     * @ApiRoute    (/api/wechatpay/profitsharing)
     * @ApiHeaders  (name=token, type=string, required=true, description="请求的TOKEN")
     * @ApiHeaders  (name=sub_mchid, type=string, required=true, description="子商户的商户号,必填")
     * @ApiHeaders  (name=sub_appid, type=string, required=true, description="子商户的appid")
     * @ApiHeaders  (name=transaction_id, type=string, required=true, description="微信支付订单号")
     * @ApiHeaders  (name=out_order_no, type=string, required=true, description="商户系统内部的分账单号")
     * @ApiHeaders  (name=amount, type=float, required=true, description="分账金额,单位:元")
     * @ApiSummary  (微信订单支付成功后,由电商平台发起分账请求,将结算后的资金分给分账接收方。)
     */
     /*   注意:

        • 微信订单支付成功后,服务商代特约商户发起分账请求,将结算后的钱分到分账接收方。

        • 对同一笔订单最多能发起50次分账请求,每次请求最多分给5个接收方。

        • 此接口采用异步处理模式,即在接收到商户请求后,会先受理请求再异步处理,最终的分账结果可以通过查询分账接口获取。

        • 分账资金的冻结期默认是180天。从订单支付成功之日起,180天内需要发起分账,若180天内未发起分账,待分账资金将会自动解冻给分账方。
     */
    public function profitsharing()
    {
        $data = $this->request->post();
        if($data['sub_mchid']=='')
        {
            $this->error('子商户的商户号不能为空');
        }
        try {
            $resp = $this->client->request(
                'POST',
                'https://api.mch.weixin.qq.com/v3/profitsharing/orders', //请求URL
                [
                    // JSON请求体
                    'json' => [
                        "sub_mchid" => $data['sub_mchid'],            //分账出资的电商平台二级商户,填写微信支付分配的商户号。
                        "appid"     => Config('site.sp_appid'),  // 服务商申请的公众号appid
                        "sub_appid" => $data['sub_appid'],           // 微信分配的子商户公众账号ID
                        "transaction_id" => $data['transaction_id'],  //微信支付订单号
                        "out_order_no" => $data['out_order_no'],      //商户系统内部的分账单号
                        //分账接收方列表,可以设置出资商户作为分账接受方,最多可有50个分账接收方
                        "receivers" => [
                            [
                                "type"  => 'MERCHANT_ID',  //分账接收方类型,枚举值:MERCHANT_ID:商户PERSONAL_OPENID:个人
                                "account" => $this->merchantId, //分账接收方账号(当前为服务商自己的商户号)
                                //分账金额,单位为分,只能为整数,不能超过原订单支付金额及最大分账比例金额。
                                "amount" => $data['amount'] * 100,
                                "description" => '分账给服务商'.$this->merchantId, //分账的原因描述,分账账单中需要体现。
                            ],
                        ],
                        /*
                         * 是否解冻剩余未分资金,
                        1、如果为true,该笔订单剩余未分账的金额会解冻回分账方商户;
                        2、如果为false,该笔订单剩余未分账的金额不会解冻回分账方商户,可以对该笔订单再次进行分账。
                        */
                        "unfreeze_unsplit"  => true    //是否解冻剩余未分资金,
                    ],
                    'headers' => [ 'Accept' => 'application/json' ]
                ]
            );
            $statusCode = $resp->getStatusCode();
            if ($statusCode == 200) { //处理成功
                $data = json_decode($resp->getBody()->getContents(),true);
                $this->success('success',$data);
            } else if ($statusCode == 204) { //处理成功,无返回Body
                $this->success('success');
            }
        } catch (RequestException $e) {
            // 进行错误处理
            $data = [
                'status'=>  $e->getResponse()->getStatusCode(),
                'message'=> json_decode($e->getResponse()->getBody(),true)
            ];
            $this->error('ERROR',$data);
        }
    }

④. 敏感字符进行加密操作

    /*
     * 敏感字符进行加密操作
     */
    private function getEncrypt($str) {
        //$str是待加密字符串
        $public_key_path = ".".Config('site.wechatpayCertificate');
        $public_key = file_get_contents($public_key_path);
        $encrypted = '';
        if (openssl_public_encrypt($str, $encrypted, $public_key, OPENSSL_PKCS1_OAEP_PADDING)) {
            //base64编码
            $sign = base64_encode($encrypted);
        } else {
            throw new Exception('encrypt failed');
        }
        return $sign;
    }

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 登录微信支付服务商平台,进入“API商户平台”界面,在左侧菜单栏中选择“开发配置”,点击“APIv3密钥”页面中的“下载证书”按钮,下载证书至本地。 2. 在你的应用中引入微信支付官方提供的SDK文件,包括wxpay.js和wxpay.php两个文件。将这两个文件放置在正确的路径下,确保能够被应用调用。 3. 在你的代码中引入wxpay.php文件,使用以下代码初始化微信支付服务商: ```php require_once "wxpay.php"; $config = array( 'mch_id' => '你的服务商商户号', 'appid' => '你的服务商公众号appid', 'key' => '你的服务商支付密钥', 'cert_path' => '你下载的证书文件路径/apiclient_cert.pem', 'key_path' => '你下载的证书文件路径/apiclient_key.pem' ); $wxpay = new Wxpay($config); ``` 4. 确定用户需要支付的金额和商品信息等相关信息,构建支付请求参数,例如: ```php $params = array( 'body' => '商品描述', 'out_trade_no' => '商户订单号', 'total_fee' => 100, // 单位:分 'notify_url' => '支付回调通知地址', 'trade_type' => 'JSAPI', 'openid' => '用户openid', 'sub_mch_id' => '子商户号' ); ``` 其中,'trade_type' => 'JSAPI'表示使用JSAPI支付方式,'openid' => '用户openid'表示用户在公众号中的openid,'sub_mch_id' => '子商户号'表示子商户号。 5. 调用统一下单接口,生成预支付订单。例如: ```php $result = $wxpay->unifiedOrder($params); ``` 6. 根据返回结果,生成JSAPI支付所需的参数,例如: ```php $order_params = array( 'appId' => $wxpay->appId, 'timeStamp' => time(), 'nonceStr' => $wxpay->getNonceStr(), 'package' => 'prepay_id=' . $result['prepay_id'], 'signType' => 'RSA', ); $order_params['paySign'] = $wxpay->getPaySign($order_params); // 生成签名 ``` 其中,'appId'、'timeStamp'、'nonceStr'、'package'、'signType'都是JSAPI支付所需的参数,'paySign'为签名值。 7. 将生成的JSAPI支付所需的参数返回给前端,用于调起微信支付界面。 8. 在支付完成后,微信会向你的'notify_url'地址发送支付结果通知。在该地址中,需要对支付结果进行处理,例如: ```php $xml = file_get_contents('php://input'); $result = $wxpay->notify($xml); // 处理支付结果 if ($result) { // 支付成功 } else { // 支付失败 } ``` 以上为微信服务商支付V3版引入官方SDK的教程。需要注意的是,在使用微信支付服务商支付时,需要先在微信支付服务商平台中完成相关配置,包括商户号、支付密钥、证书等。同时,还需要在调用支付接口前获取用户的openid等信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值