微信支付服务商分账-请求单次分账

注意事项

服务商请求单次分账与普通商户请求单次分账的区别。1

下预付单时 务必要添加 profit_sharing 为 Y 否则该笔订单不支持分账。 参考链接 https://pay.weixin.qq.com/wiki/doc/api/allocation.php?chapter=26_3
订单支付成功后不能立刻执行分账逻辑 要任务操作 大于订单支付时间1分钟以上 有QPS限制 建议做好日志警告
<?php

namespace app\api\lib\Weixin;

use think\Controller;

class Weixin extends Controller
{
    private $sep_url;                           // 单次分账请求URL
    private $mch_id;                            // 商户号
    private $appid;                             // 公众号appid
    private $mch_secrect;                       // 此处是商户key!!!

    function __construct()
    {
        $this->sep_url = 'https://api.mch.weixin.qq.com/secapi/pay/profitsharing';
        $this->mch_id = config('wechat.pay_config.mch_id');
        $this->appid = config('wechat.pay_config.app_id');
        $this->mch_secrect = config('wechat.pay_config.key');
    }

    /**
     * Notes: 请求单次分账
     * User: googol
     * Date: 2020-07-15
     * Time: 10:28
     * Url:
     * @param $transaction_id   微信支付交易单号
     * @param $out_order_no     商户系统内部的分账单号,在商户系统内部唯一(单次分账、多次分账、完结分账应使用不同的商户分账单号),同一分账单号多次请求等同一次。只能是数字、大小写字母_-|*@
     * @param $sub_mch_id       微信支付分配的子商户号
     */
    function requestsingleaccountsplitting($transaction_id, $out_order_no, $sub_mch_id)
    {
        $receivers = $this->receivers($out_order_no);
        if ($receivers['code'] == 0) return ['code' => '分账失败!'];
        $tmp_splitting_data = [
            'mch_id' => $this->mch_id,
            'sub_mch_id' => $sub_mch_id,    // 微信支付分配的子商户号
            'appid' => $this->appid,
            'sub_appid' => $this->appid,    // 微信分配的子商户公众账号ID
            'nonce_str' => $this->get_nonce_str(),
            'sign_type' => 'HMAC-SHA256',
            'transaction_id' => $transaction_id,
            'out_order_no' => $out_order_no,
            'receivers' => $receivers['res']
        ];
        $tmp_splitting_data['sign'] = $this->make_sign($tmp_splitting_data, $this->mch_secrect);
        $xml = $this->array_to_xml($tmp_splitting_data);
        $do_arr = $this->curl_post_ssl($this->sep_url, $xml);
        $result = $this->xml_to_array($do_arr);
        return $result;
    }

    /**
     * Notes: 获取随机数
     * @param int $length
     * @return string
     */
    private function get_nonce_str($length = 32)
    {
        $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
        $str = "";
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }

    /**
     * Notes: 获取分账详细列表信息
     * User: googol
     * @param $out_order_no     商户内部的分账单号
     */
    private function receivers($out_order_no)
    {

        /*CREATE TABLE `m_separateaccounts_order` (
          `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
          `trade` varchar(32) NOT NULL DEFAULT '' COMMENT '分账单号',
          `m_id` int(11) NOT NULL DEFAULT '0' COMMENT '机器id',
          `s_id` int(11) NOT NULL DEFAULT '0' COMMENT '店铺id',
          `agent_id` int(11) NOT NULL COMMENT '代理id',
          `account` varchar(64) NOT NULL DEFAULT '' COMMENT '分账接收openid',
          `agent_type` tinyint(1) NOT NULL DEFAULT '3' COMMENT '0平台 1一级代理 2二级代理',
          `proportion` int(5) NOT NULL DEFAULT '0' COMMENT '比例',
          `payment` decimal(12,2) NOT NULL DEFAULT '0.00' COMMENT '实际付款金额',
          `shareamount` int(11) NOT NULL DEFAULT '0' COMMENT '分得金额 分',
          `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '结算状态 0待分账 1已分账 2分账失败',
          `cdate` varchar(12) NOT NULL DEFAULT '' COMMENT '日期',
          `regdate` int(11) NOT NULL COMMENT '时间',
          `shareddate` int(11) NOT NULL DEFAULT '0' COMMENT '分账时间',
          PRIMARY KEY (`id`)
        ) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8 COMMENT='分账订单';*/

        $out_order = db('separateaccounts_order')
            ->where(['trade' => $out_order_no, 'status' => 0])
            ->field('payment, account, shareamount')
            ->select();
        if (!empty($out_order)) {
            $receivers_arr = [];
            foreach ($out_order as $key => $val) {
                $receivers_arr[$key]['type'] = 'PERSONAL_OPENID';
                $receivers_arr[$key]['account'] = $val['account'];
                $receivers_arr[$key]['amount'] = $val['shareamount'];
                $receivers_arr[$key]['description'] = 'Agency sub account.';
            }
            return ['code' => 1, 'res' => json_encode($receivers_arr)];
        }
        return ['code' => 0];
    }

    /**
     * Notes: 生成sign
     * @param $arr
     * @param $secret
     * @return string
     */
    private function make_sign($arr, $secret)
    {
        //签名步骤一:按字典序排序参数
        ksort($arr);
        $str = $this->to_url_params($arr);
        //签名步骤二:在str后加入KEY
        $str = $str . "&key=" . $secret;
        //签名步骤三:HMAC-SHA256 类型  加密的字符串 key是商户秘钥
        $str = hash_hmac('sha256', $str, $this->mch_secrect);
        //签名步骤四:所有字符转为大写
        $result = strtoupper($str);
        return $result;
    }

    /**
     * Notes: 数组转字符串
     * @param $arr
     * @return string
     */
    private function to_url_params($arr)
    {
        $str = "";
        foreach ($arr as $k => $v) {
            if (!empty($v) && ($k != 'sign')) {
                $str .= "$k" . "=" . $v . "&";
            }
        }
        $str = rtrim($str, "&");
        return $str;
    }

    /**
     * Notes: 数组转XML
     * @param $arr
     * @return string
     */
    private function array_to_xml($arr){
        $xml = '<?xml version="1.0" encoding="UTF-8"?><xml>';
        foreach ($arr as $key => $val) {
            $xml.="<".$key.">$val</".$key.">";
        }
        $xml.="</xml>";
        return $xml;
    }

    /**
     * Notes: XML转数组
     * @param $xml
     * @return mixed
     */
    private function xml_to_array($xml){
        libxml_disable_entity_loader(true);
        $arr= json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $arr;
    }

    /**
     * Notes: POST 请求 此处需要证书
     * @param $xml
     * @param $url
     * @param int $second
     * @return bool|string
     */
    function curl_post_ssl($url, $vars, $second = 30, $aHeader = array())
    {
        $isdir = __DIR__ . "/../../../../cert/";//证书位置

        $ch = curl_init();//初始化curl
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);//设置执行最长秒数
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);//要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_URL, $url);//抓取指定网页
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);// 终止从服务端进行验证
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);//
        curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');//证书类型
        curl_setopt($ch, CURLOPT_SSLCERT, $isdir . 'apiclient_cert.pem');//证书位置
        curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');//CURLOPT_SSLKEY中规定的私钥的加密类型
        curl_setopt($ch, CURLOPT_SSLKEY, $isdir . 'apiclient_key.pem');//证书位置
//        curl_setopt($ch, CURLOPT_CAINFO, 'PEM');
//        curl_setopt($ch, CURLOPT_CAINFO, $isdir . 'rootca.pem');
        if (count($aHeader) >= 1) {
            curl_setopt($ch, CURLOPT_HTTPHEADER, $aHeader);//设置头部
        }
        curl_setopt($ch, CURLOPT_POST, 1);//post提交方式
        curl_setopt($ch, CURLOPT_POSTFIELDS, $vars);//全部数据使用HTTP协议中的"POST"操作来发送

        $data = curl_exec($ch);//执行回话
        if ($data) {
            curl_close($ch);
            return $data;
        } else {
            $error = curl_errno($ch);
            echo "call faild, errorCode:$error\n";
            curl_close($ch);
            return false;
        }
    }
}

  1. 服务商添加分账方与普通商户添加分账方大致一致 只需要额外注意服务商分账需要服务商添加子商户的子商户号与公众账号ID ↩︎

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
PHP微信请求多次分账的步骤如下: 1. 首先,需要确保你已经在微信商户平台上有一个账号,并且已经完成了相关的认证和设置。 2. 在你的PHP项目中,首先需要引入微信支付的SDK,可以利用composer进行安装。 3. 在你的代码中,首先需要获取到微信支付的配置信息,包括商户号、商户密钥等。 4. 接下来,构建一个分账请求参数的数组,包括接收方账号、分账金额等信息。 5. 利用微信支付SDK中提供的接口,发送分账请求。可以使用“单次分账”接口进行单笔分账,或者使用“多次分账”接口进行多笔分账。 6. 如果使用的是“多次分账”接口,需要在代码中编写一个循环,多次调用分账接口,直到所有的分账请求都发送成功。 7. 在每次分账请求发送成功后,可以根据微信支付返回的结果进行判断,如果返回结果中的“return_code”为“SUCCESS”,表示该笔分账请求已经成功发送至微信支付服务器。 8. 一旦分账请求发送成功,可以根据需要保存这次分账的相关信息,例如分账请求的时间、金额等。 9. 如果在分账请求发送过程中出现了错误,可以根据微信支付返回的错误代码和错误信息进行调试和处理。 10. 最后,在所有的分账请求都发送成功后,可以对分账结果进行确认和核对,确保所有的分账金额都正确到账。 需要注意的是,进行多次分账请求时,需要保证每次请求分账总金额不超过该笔订单的总金额。如果分账请求金额超过订单金额,请求将会被拒绝。同时,每次分账请求的总金额加起来必须等于订单总金额,否则请求也会被拒绝。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值