ecshop | php接入支付宝申报海关接口的总结【避坑】

简介: ecshop | php接入支付宝申报海关接口的总结【避坑】

第一次对接支付宝的报关接口,害!不明白这个流程是很难接上的,搞了一天半,终于给搞出来了。在这里留下一点文字吧。

代码前工作:

  1. 用支付宝的密钥生成工具生成一对2048的公私钥,这对公私钥用来完成支付宝支付的加签和验签。在这里插入图片描述

把公钥上传到 开放平台密钥中对应使用的应用接口加签方式中,私钥是用在支付接口中加签的,保存好!
在这里插入图片描述
在这里插入图片描述

  1. 用支付宝的密钥生成工具生成一对1024的公私钥,这对公私钥用来完成报关流程(1024位是技术客服推荐的),把公钥上传至 mapi网关产品密钥和老板wap支付密钥 的开发者公钥中去,密钥保存好,用来给报关文档中的sign字段加签。

在这里插入图片描述

现在开始写代码

看文档可以知道,要确认海关申报是否成功要接上两个接口:报关接口报关查询接口,当看到报关查询接口的返回参数中的status==succ时,才算报关成功。那就一步一步来吧,先接报关接口:

  1. 把文档中所需要的参数查询出来,放到一个数组里,注意:sign和sign_type字段先别写进去。
//推送支付宝
if($_REQUEST['act']=='push_ali'){
    //需要进行加签的数据
    $data = array(
        'amount' => $amount,                                    //报关金额
        'buyer_name' => $buyer_name,                            //订购人姓名
        'buyer_id_no' => $buyer_id_no,                          //订购人身份证
        'customs_place' => 'zongshu',                            //海关编号
        'merchant_customs_code' => 'xxxxx',              //商户海关备案号
        'merchant_customs_name' => 'xxxxx有限公司',      //商户海关备案名称
        'out_request_no' => $out_request_no,                   //报关流水号(订单号)
        'partner' => '2088xxxxxxxxxxxx',                      //合作者身份ID,2088开头
        'service' => 'alipay.acquire.customs',                //接口名称
        'trade_no' => $trade_no,                                //支付宝流水号
        '_input_charset' => 'utf-8',                          //参数编码字符集
    );
    
    $data = argSort($data);//对数组排序
    //把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
    $sign_data= createLinkstring($data);
    //生成sign参数
    $sign = urlencode(rsaSign($sign_data));

    //签名结果与签名方式 加入请求提交参数组中
    $data['sign_type'] = 'RSA';
    $data['sign'] = $sign;

    $url = 'https://mapi.alipay.com/gateway.do?';
    $data = createLinkstring($data);
    //发送get请求的url
    $getUrl = $url.$data;
    $response = getHttpResponseGET($getUrl);
    //接受报关返回的xml,转换成数组
    $xml = simplexml_load_string($response);
    $data = json_decode(json_encode($xml),TRUE);
    
    /判断报关是否成功,成功则进行报关查询
    if($data['is_success'] == 'T'){
        //继续写报关查询接口
    }else{
        //返回错误代码
    }

argSort()、 createLinkstring()、rsaSign()、getHttpResponseGET()是支付宝官方提供的demo中的方法,你不想用他的也可以自己写,简单看一下吧

/**
 * 对数组排序
 * @param $para 排序前的数组
 * return 排序后的数组
 */
function argSort($para) {
    ksort($para);
    reset($para);
    return $para;
}

/**
 * 把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
 * @param $para 需要拼接的数组
 * return 拼接完成以后的字符串
 */
function createLinkstring($para) {
    $arg  = "";
    while (list ($key, $val) = each ($para)) {
        $arg.=$key."=".$val."&";
    }
    //去掉最后一个&字符
    $arg = substr($arg,0,count($arg)-2);

    //如果存在转义字符,那么去掉转义
    if(get_magic_quotes_gpc()){$arg = stripslashes($arg);}
    return $arg;
}

/**
 * RSA签名
 * @param $data 待签名数据
 * @param $private_key 商户私钥字符串
 * return 签名结果
 */
function rsaSign($data) {
    //商户私钥
    $private_key = "上面生成的1024位的密钥写在这里";
    //以下为了初始化私钥,保证在您填写私钥时不管是带格式还是不带格式都可以通过验证。
    $private_key=str_replace("-----BEGIN RSA PRIVATE KEY-----","",$private_key);
    $private_key=str_replace("-----END RSA PRIVATE KEY-----","",$private_key);
    $private_key=str_replace("\n","",$private_key);

    $private_key="-----BEGIN RSA PRIVATE KEY-----".PHP_EOL .wordwrap($private_key, 64, "\n", true). PHP_EOL."-----END RSA PRIVATE KEY-----";

    $res=openssl_get_privatekey($private_key);

    if($res)
    {
        openssl_sign($data, $sign,$res);
    }
    else {
        echo "您的私钥格式不正确!"."<br/>"."The format of your private_key is incorrect!";
        exit();
    }
    openssl_free_key($res);
    //base64编码
    $sign = base64_encode($sign);
    return $sign;
}

/**
 * 远程获取数据,GET模式
 * 注意:
 * 1.使用Crul需要修改服务器中php.ini文件的设置,找到php_curl.dll去掉前面的";"就行了
 * 2.文件夹中cacert.pem是SSL证书请保证其路径有效,目前默认路径是:getcwd().'\\cacert.pem'
 * @param $url 指定URL完整路径地址
 * @param $cacert_url 指定当前工作目录绝对路径
 * return 远程输出的数据
 */
function getHttpResponseGET($url) {
    $curl = curl_init($url);
    curl_setopt($curl, CURLOPT_HEADER, 0 ); // 过滤HTTP头
    curl_setopt($curl,CURLOPT_RETURNTRANSFER, 1);// 显示输出结果
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);//SSL证书认证
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);//严格认证
    $responseText = curl_exec($curl);
    //var_dump( curl_error($curl) );//如果执行curl过程中出现异常,可打开此开关,以便查看异常内容
    curl_close($curl);

    return $responseText;
}

这里有几点要注意一下:

  • rsaSign()方法中的private_key是使用1024位的密钥,用来给数据加签用的。
  • 可以post发送请求也可以发送get请求,官方技术推荐get方式。
  • rsa加签后的sign值是需要urlencode一下再进行拼装。

在这里插入图片描述
请求发送后,打印一下支付宝的同步返回,如果 is_success为 T ,则报关支付宝成功,但这不代表业务处理成功。实际上,海关返回信息并不是同步返回的,他会间隔10分钟左右。所以继续接入报关查询接口

if($data['is_success'] == 'T'){
        $response = $data['response'];//返回数组中的response数组
        $alipay_data = $response['alipay'];//response中的alipay数组

        //报关查询sign值加签参数
        $datas = array(
            'service' => 'alipay.overseas.acquire.customs.query',
            'partner' => '2088xxxxxxxxxxxx',
            '_input_charset' => 'utf-8',
            'out_request_nos' => $alipay_data['out_request_no']
        );
        //排序
        $datas = argSort($datas);
        //把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
        $sign_datas= createLinkstring($datas);
        //生成sign参数
        $sign = urlencode(rsaSign($sign_datas));

        //签名结果与签名方式 加入请求提交参数组中
        $datas['sign_type'] = 'RSA';
        $datas['sign'] = $sign;
        //var_dump($datas);

        $datas = createLinkstring($datas);
        //报关查询发送get请求的url
        $getUrls = $url.$datas;

        //接受报关返回的xml,转换成数组
        $response = getHttpResponseGET($getUrls);
        $xml = simplexml_load_string($response);
        $datas = json_decode(json_encode($xml),TRUE);
        //找customs_declare中的status,succ是海关申报成功

在这里插入图片描述
这一步和报关其实差不多,主要是发送的数据不一样。接受到的结果如图,当status对于succ是,恭喜你,成功了!既然工作已经完成了,那就盘点一下我视为障碍的地方:
在这里插入图片描述

  • sign值:文档中写着参见9,签名机制,我找了半天没找到这个9在哪里,后来还是问客服解决的。
  • 刚开始说道的生成的两对公私钥,文档中没有看到对应的提醒,商户中心也没有对应的提醒,害!新手很难的啊。
  • 支付宝的公钥是会变的,每次更改商户的密钥对他都会变化一次。(刚开始我以为他不会变。。。。)
  • 如果你打印出来的数据显示不全,打开php.ini,在xdebug的地方加上:
xdebug.var_display_max_children=128 //允许一个数组最多显示多少个元素
xdebug.var_display_max_data=1024 //允许一个字符串变量最多显示多少个字节
xdebug.var_display_max_depth=10 //允许一个数组最多显示多少个维度

在简单说一下我对密钥对的理解:
RSA 是一种非对称的签名算法,即签名密钥(私钥)与验签密钥(公钥)是不一样的, 私钥用于签名,公钥用于验签。

私钥就是锁,公钥就是钥匙,只有拿对应的钥匙(公钥)才能解对应的锁(私钥)。

功夫不负有心人,bug总会被解决的,自己看不懂就多问问多看看,看多了就熟了,问多了就懂了!

目录
相关文章
原生php实现列表接口+分页接口+排序接口组合使用+包括测试数据(不加任何封装)
原生php实现列表接口+分页接口+排序接口组合使用+包括测试数据(不加任何封装)
原生php实现列表接口+分页接口+排序接口组合使用+包括测试数据(不加任何封装)
|
2天前
|
监控 PHP Python
1688快速获取整店铺列表 采集接口php Python
在电子商务的浪潮中,1688平台作为中国领先的批发交易平台,为广大商家提供了一个展示和销售商品的广阔舞台;然而,要在众多店铺中脱颖而出,快速获取商品列表并进行有效营销是关键。
|
10天前
|
PHP 数据安全/隐私保护
【PHP开发专栏】PHP接口与抽象类的应用
【4月更文挑战第30天】本文探讨了PHP中接口与抽象类的使用,包括定义、实现和比较。接口用于规定实现类必须提供的方法签名,而抽象类则可以包含方法实现和抽象方法。一个类可实现多个接口,但只能继承一个抽象类。根据需求,若需定义不相关类的共同方法,选择接口;若需提供共享属性和非抽象方法,选择抽象类。通过实战应用示例,展示了如何在动物园管理系统中结合接口和抽象类进行设计。理解两者有助于提升代码的复用性和可维护性。
|
20天前
|
XML JSON API
快速淘宝商品详情页面API接口传输 php
PI(Application Programming Interface,应用程序接口)是一组预定义的函数、协议和工具,用于构建软件应用程序之间的交互。它允许不同的软件系统和应用通过统一的接口进行数据交换和通信
|
21天前
|
PHP
PHP面向对象编程精要:接口、抽象类和继承
PHP面向对象编程涉及接口、抽象类和继承。接口定义了类必须实现的方法,抽象类包含抽象方法,不可实例化,而继承允许子类扩展父类属性和行为。通过案例展示了如何使用interface、abstract和extends关键字。这些概念增强了代码的灵活性、可维护性和可扩展性。
14 1