php 接入建行惠市宝的验签规则

官方提供的代码包,只有java代码示例,费了九牛二虎之力,在群里沟通了应该有一个星期,把建行的验签规则的php写法完成了。大家各取所需。

<?php

namespace app\data\repository\api;

use app\data\repository\common\CommonRepo;
use think\admin\extend\CodeExtend;
use think\admin\extend\HttpExtend;

/**
 * 惠市宝接口封装(建行)
 * Class Huishibao
 * @package app\data\repository\api
 */
class Huishibao
{
    /**
     * 生成订单接口
     *
     * @param string $order_no
     * @param float $amount
     * @param string $openid
     * @return array
     */
    public function reqGatherPlaceorder($order_no, $amount, $openid)
    {
        $url = 'https://xxx/gatherPlaceorder';
        $amount = CommonRepo::returnMoney($amount);

        $param = [
            'Ittparty_Stm_Id' => config('huishibao.Ittparty_Stm_Id'), // 发起渠道编号,默认送5个0
            'Py_Chnl_Cd' => config('huishibao.Py_Chnl_Cd'), // 支付渠道代码,默认送25个0
            'Ittparty_Tms' => $this->time_ms(), // 发起方时间戳,yyyyMMddHHmmssfff 年月日, 时分秒,毫秒
            'Ittparty_Jrnl_No' => CommonRepo::create_uuid(), // 发起方流水号,该笔直连交易的客户方流水号(不允许重复)
            'Mkt_Id' => config('huishibao.Mkt_Id'),  // 14位市场编号,该字段由银行在正式上线前提供,测试阶段有测试数据
            'Main_Ordr_No' => $order_no, // 客户方主订单流水号,不允许重复
            'Sub_Appid' => config('huishibao.Sub_Appid'),
            'Sub_Openid' => $openid,
            // 'Pymd_Cd' => '03', // 支付方式,01 PC端(收银台) 02 线下支付 03 移动端H5页面 (收银台,支持app) 05 微信小程序 06 对私网银 07 聚合二维码 08龙支付 09被扫 11数字电子钱包 12无感支付 13共享钱包 14支付宝小程序
            'Pymd_Cd' => '05', // 支付方式,01 PC端(收银台) 02 线下支付 03 移动端H5页面 (收银台,支持app) 05 微信小程序 06 对私网银 07 聚合二维码 08龙支付 09被扫 11数字电子钱包 12无感支付 13共享钱包 14支付宝小程序
            'Py_Ordr_Tpcd' => '03', // 订单类型,03在途订单(只有是否支持在途模式为“是”时才可以使用,品类管控市场订单类型必须为03),04普通订单
            'Ccy' => '156', // 币种,156人民币
            'Ordr_Tamt' => $amount, // 订单总金额
            'Txn_Tamt' => $amount, // 交易总金额
            'Pay_Dsc' => '在线支付',
            'Orderlist' => [ // 订单类型为“04普通订单”时,子订单列表至少要有1条记录,订单类型为“02 消费券购买订单”或“03 在途订单”时,子订单列表固定为1条记录
                [
                    'Mkt_Mrch_Id' => config('huishibao.Mkt_Mrch_Id'), // 商家编号,20位商家编号,该字段由银行在正式上线前提供,测试阶段有测试数据,订单类型为“03在途订单”时,填写市场方商家编号(市场编号+000000)
                    'Cmdty_Ordr_No' => $order_no, // 客户方子订单编号不允许重复
                    'Ordr_Amt' => $amount, // 订单商品总金额,即应付金额,所有商品订单金额之和等于主订单金额;
                    'Txnamt' => $amount, // 消费者实付金额,所有商品订单金额之和等于主交易总金额
                ]
            ],
            'Vno' => '4', // 版本号,默认填写版本4
        ];

        $res_data = $this->commReq($url, $param);
        return $res_data;
    }

    /**
     * 查询订单信息
     *
     * @param string $order_no
     * @return array
     */
    public function reqOrderInfQuery($order_no)
    {
        $url = 'https://xxx/OrderInfQuery';

        $param = [
            'Ittparty_Stm_Id' => config('huishibao.Ittparty_Stm_Id'), // 发起渠道编号,默认送5个0
            'Py_Chnl_Cd' => config('huishibao.Py_Chnl_Cd'), // 支付渠道代码,默认送25个0
            'Ittparty_Tms' => $this->time_ms(), // 发起方时间戳,yyyyMMddHHmmssfff 年月日, 时分秒,毫秒
            'Ittparty_Jrnl_No' => CommonRepo::create_uuid(), // 发起方流水号,该笔直连交易的客户方流水号(不允许重复)
            'Mkt_Id' => config('huishibao.Mkt_Id'),  // 14位市场编号,该字段由银行在正式上线前提供,测试阶段有测试数据
            'Main_Ordr_No' => $order_no,
            'Vno' => '4', // 版本号,默认填写版本4
        ];

        $res_data = $this->commReq($url, $param);
        return $res_data;
    }

    /**
     * 确认收货接口
     * 1、请避免在0点前后5分钟内进行确认收货。
     * 2、本接口不适用于品类管控的市场。
     *
     * @param string $order_no_huishibao 惠市宝生成的订单编号
     * @return array
     */
    public function reqMergeNoticeArrival($order_no_huishibao)
    {
        $url = 'https://xxx/mergeNoticeArrival';

        $param = [
            'Ittparty_Stm_Id' => config('huishibao.Ittparty_Stm_Id'), // 发起渠道编号,默认送5个0
            'Py_Chnl_Cd' => config('huishibao.Py_Chnl_Cd'), // 支付渠道代码,默认送25个0
            'Ittparty_Tms' => $this->time_ms(), // 发起方时间戳,yyyyMMddHHmmssfff 年月日, 时分秒,毫秒
            'Ittparty_Jrnl_No' => CommonRepo::create_uuid(), // 发起方流水号,该笔直连交易的客户方流水号(不允许重复)
            'Mkt_Id' => config('huishibao.Mkt_Id'),  // 14位市场编号,该字段由银行在正式上线前提供,测试阶段有测试数据
            'Prim_Ordr_No' => $order_no_huishibao, // 惠市宝生成的订单编号
            'Vno' => '4', // 填写版本为4
        ];

        $res_data = $this->commReq($url, $param);
        return $res_data;
    }

    /**
     * 查询支付结果接口
     *
     * @param string $order_no
     * @return array
     */
    public function reqGatherEnquireOrder($order_no)
    {
        $url = 'https://xxx/gatherEnquireOrder';

        $param = [
            'Ittparty_Stm_Id' => config('huishibao.Ittparty_Stm_Id'), // 发起渠道编号,默认送5个0
            'Py_Chnl_Cd' => config('huishibao.Py_Chnl_Cd'), // 支付渠道代码,默认送25个0
            'Ittparty_Tms' => $this->time_ms(), // 发起方时间戳,yyyyMMddHHmmssfff 年月日, 时分秒,毫秒
            'Ittparty_Jrnl_No' => CommonRepo::create_uuid(), // 发起方流水号,该笔直连交易的客户方流水号(不允许重复)
            'Mkt_Id' => config('huishibao.Mkt_Id'),  // 14位市场编号,该字段由银行在正式上线前提供,测试阶段有测试数据
            'Main_Ordr_No' => $order_no,
            'Vno' => '4', // 版本号,默认填写版本4
        ];

        $res_data = $this->commReq($url, $param);
        return $res_data;
    }

    /**
     * 订单全额退款接口
     *
     * @param string $refund_no 退款流水号
     * @param string $order_no_huishibao_flow 惠市宝生成的交易流水号
     * @return array
     */
    public function reqRefundOrderFull($refund_no, $order_no_huishibao_flow)
    {
        $url = 'https://xxx/refundOrder';

        $param = [
            'Ittparty_Stm_Id' => config('huishibao.Ittparty_Stm_Id'), // 发起渠道编号,默认送5个0
            'Py_Chnl_Cd' => config('huishibao.Py_Chnl_Cd'), // 支付渠道代码,默认送25个0
            'Ittparty_Tms' => $this->time_ms(), // 发起方时间戳,yyyyMMddHHmmssfff 年月日, 时分秒,毫秒
            'Ittparty_Jrnl_No' => CommonRepo::create_uuid(), // 发起方流水号,该笔直连交易的客户方流水号(不允许重复)
            'Mkt_Id' => config('huishibao.Mkt_Id'),  // 14位市场编号,该字段由银行在正式上线前提供,测试阶段有测试数据
            'Cust_Rfnd_Trcno' => $refund_no, // 该字段由发起方生成,请求退款时不允许重复(当出现请求超时情况时,客户可凭借此字段重复发起退款,对于同一笔客户方退款流水号,惠市宝确保只发生一次退款)。重复流水号可查询该流水号退款请求结果;不同流水号为发起新请求。
            'Py_Trn_No' => $order_no_huishibao_flow, // 惠市宝生成,与该订单的支付动作唯一匹配
            'Vno' => '4', // 版本号,默认填写版本4
        ];

        $res_data = $this->commReq($url, $param);
        return $res_data;
    }

    /**
     * 订单部分退款接口
     *
     * @param string $refund_no 退款流水号
     * @param string $order_no_huishibao_flow 惠市宝生成的交易流水号
     * @param string $order_no_sub 惠市宝生成的子订单编号
     * @param string $amount 部分退款金额
     * @return array
     */
    public function reqRefundOrder($refund_no, $order_no_huishibao_flow, $order_no_sub, $amount)
    {
        $url = 'https://xxx/refundOrder';
        $amount = CommonRepo::returnMoney($amount);

        $param = [
            'Ittparty_Stm_Id' => config('huishibao.Ittparty_Stm_Id'), // 发起渠道编号,默认送5个0
            'Py_Chnl_Cd' => config('huishibao.Py_Chnl_Cd'), // 支付渠道代码,默认送25个0
            'Ittparty_Tms' => $this->time_ms(), // 发起方时间戳,yyyyMMddHHmmssfff 年月日, 时分秒,毫秒
            'Ittparty_Jrnl_No' => CommonRepo::create_uuid(), // 发起方流水号,该笔直连交易的客户方流水号(不允许重复)
            'Mkt_Id' => config('huishibao.Mkt_Id'),  // 14位市场编号,该字段由银行在正式上线前提供,测试阶段有测试数据
            'Cust_Rfnd_Trcno' => $refund_no, // 该字段由发起方生成,请求退款时不允许重复(当出现请求超时情况时,客户可凭借此字段重复发起退款,对于同一笔客户方退款流水号,惠市宝确保只发生一次退款)。重复流水号可查询该流水号退款请求结果;不同流水号为发起新请求。
            'Py_Trn_No' => $order_no_huishibao_flow, // 惠市宝生成,与该订单的支付动作唯一匹配
            'Rfnd_Amt' => $amount, // 订单全额退款时不需要送,订单部分退款时必须送此值,且值等于所有子订单的退款金额之和
            'Sub_Ordr_List' => [
                [
                    'Sub_Ordr_Id' => $order_no_sub, // 惠市宝生成的子订单编号
                    'Rfnd_Amt' => $amount,
                ],
            ],
            'Vno' => '4', // 版本号,默认填写版本4
        ];

        $res_data = $this->commReq($url, $param);
        return $res_data;
    }

    /**
     * 分账状态查询接口
     *
     * @param string $order_no_huishibao_flow 惠市宝生成的交易流水号
     * @return array
     */
    public function reqSubAccountEnquire($order_no_huishibao_flow)
    {
        $url = 'https://xxx/subAccountEnquire';

        $param = [
            'Ittparty_Stm_Id' => config('huishibao.Ittparty_Stm_Id'), // 发起渠道编号,默认送5个0
            'Py_Chnl_Cd' => config('huishibao.Py_Chnl_Cd'), // 支付渠道代码,默认送25个0
            'Ittparty_Tms' => $this->time_ms(), // 发起方时间戳,yyyyMMddHHmmssfff 年月日, 时分秒,毫秒
            'Ittparty_Jrnl_No' => CommonRepo::create_uuid('', false), // 发起方流水号,该笔直连交易的客户方流水号(不允许重复)
            'Mkt_Id' => config('huishibao.Mkt_Id'),  // 14位市场编号,该字段由银行在正式上线前提供,测试阶段有测试数据
            'Py_Trn_No' => $order_no_huishibao_flow,
            'Vno' => '5', // 填写版本为4
        ];

        $res_data = $this->commReq($url, $param);
        return $res_data;
    }

    /**
     * 在途信息确认接口
     *
     * @param string $order_no_huishibao_flow 惠市宝生成的交易流水号
     * @param array $part_list 分账方列表 [[ 'Seq_No' => 1, 'Mkt_Mrch_Id' => '41060860800392000000', 'Amt' => 1 ]]
     * @return array
     */
    public function reqConfirmOnTheWay($order_no_huishibao_flow, $part_list)
    {
        $url = 'https://xxx/confirmOnTheWay';

        $param = [
            'Ittparty_Stm_Id' => config('huishibao.Ittparty_Stm_Id'), // 发起渠道编号,默认送5个0
            'Py_Chnl_Cd' => config('huishibao.Py_Chnl_Cd'), // 支付渠道代码,默认送25个0
            'Ittparty_Tms' => $this->time_ms(), // 发起方时间戳,yyyyMMddHHmmssfff 年月日, 时分秒,毫秒
            'Ittparty_Jrnl_No' => CommonRepo::create_uuid(), // 发起方流水号,该笔直连交易的客户方流水号(不允许重复)
            'Mkt_Id' => config('huishibao.Mkt_Id'),  // 14位市场编号,该字段由银行在正式上线前提供,测试阶段有测试数据
            'Py_Trn_No' => $order_no_huishibao_flow,
            'Parlist' => $part_list,
            'Vno' => '3', // 填写版本为3
        ];

        $res_data = $this->commReq($url, $param);
        return $res_data;
    }

    /**
     * 返回结果验签
     *
     * @param string $str_raw
     * @param string $string
     * @return int
     */
    public function valid_encrypt($str_raw, $string)
    {
        $key = chunk_split(config('huishibao.public_key_bank'), 64, "\n");

        $key = "-----BEGIN PUBLIC KEY-----\n" . wordwrap($key) . "-----END PUBLIC KEY-----";
        $result = openssl_verify($str_raw, base64_decode($string), $key, OPENSSL_ALGO_SHA256);
        return $result;
    }

    /**
     * @return \think\Response
     */
    public function res_error()
    {
        return response('forbid', 401);
    }

    /**
     * @return void
     */
    public function res_success()
    {
        $success_data = [
            'Svc_Rsp_St' => '00', // 服务响应状态,00-成功,01-失败
        ];
        CommonRepo::echo_json($success_data);
    }

    /**
     * 写文件
     *
     * @param string $path
     * @param string $content
     * @return int|false
     */
    public function filePutContent($path, $content, $type = '')
    {
        $date = date('c');
        $content = "[{$date}] [{$type}] {$content}\n";
        return file_put_contents($path, $content, FILE_APPEND);
    }

    /**
     * @param array $param
     * @return string
     */
    public function join_param(array $param)
    {
        unset($param['Sign_Inf']);
        unset($param['Svc_Rsp_St']);
        unset($param['Svc_Rsp_Cd']);
        unset($param['Rsp_Inf']);

        ksort($param);

        $str = '';
        foreach ($param as $k => $v) {
            if ($v !== '') {
                if (is_array($v)) {
                    if (is_array($v[0])) {
                        foreach ($v as $vv) {
                            $str .= ($this->join_param($vv) . '&');
                        }
                    } else {
                        $str .= ($this->join_param($v) . '&');
                    }
                } else {
                    $str .= "$k=$v&";
                }
            }
        }
        $str = trim($str, '&');
        return $str;
    }

    /**
     * @param string $url
     * @param array $param
     * @return array
     */
    private function commReq($url, $param)
    {
        $param_str = $this->join_param($param);
        $param['Sign_Inf'] = $this->encrypt($param_str); // 签名信息,将上述参数进行拼接并用私钥生成签名
        $opt = [
            'headers' => [
                'Content-Type: application/json; charset=utf-8',
            ],
        ];
        $res = HttpExtend::post($url, CommonRepo::jsonRet($param), $opt);
        $res_data = CommonRepo::jsonDecode($res);
        return $res_data;
    }

    /**
     * @param string $str
     * @return string
     */
    private function encrypt($str)
    {
        $key = chunk_split(config('huishibao.private_key'), 64, "\n");

        $key = "-----BEGIN RSA PRIVATE KEY-----\n" . wordwrap($key) . "-----END RSA PRIVATE KEY-----";
        $res = openssl_sign($str, $sign, $key, OPENSSL_ALGO_SHA256);
        if (!$res) {
            die('sign fail');
        }
        $sign = base64_encode($sign);
        return $sign;
    }

    /**
     * 发起方时间戳,yyyyMMddHHmmssfff 年月日, 时分秒,毫秒
     * @return string
     */
    private function time_ms()
    {
        return CommonRepo::timeCut(null, 'no_space') . '000';
    }
}

 

posted @ 2024-04-28 15:13  imzhi  阅读(87)  评论(0编辑  收藏  举报