回调处理"/>
字节跳动(头条)小程序 支付,回调处理
本人针对字节调动小程序的官方开发文档真的无力吐槽,文档真的简陋。而且文档有错别字。槽点有点多。
头条因为没有自己的支付渠道,所以使用的是支付宝,利用tt.requestPayment()调起支付宝APP支付:
支付具体流程为:
一、后端通过openid和自己这边的订单号生成一个头条的订单号,具体操作可以看文档流程
二、生成调用支付宝的一个字符串,这里需要使用支付宝文档,我使用的支付宝SDK,这边支付宝文档很好的,网上资料很多。
三、最后就是组装后返回给前端调用支付宝。特别注意的是这里使用的所有appid等参数都是头条分配给商户的appid等。
首先我们通过代码来实现字节跳动小程序支付单号(trade_no)的生成。我这里用的是PHP代码,通过文档,对参数进行处理。
//这里是真的回调地址进行的简单处理
$http_type = ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')) ? 'https://' : 'http://';
$website = $http_type . $_SERVER['HTTP_HOST'];$risk_info = $this->getRealIp(); //获取用户真实IP(我在这里写了一个获取IP的方法)
$payload['app_id'] = $tt_pay_app_id; //头条支付分配给业务方的ID(不是头条小程序的appid)
$app_secret = $tt_pay_secret_key; //头条支付分配给业务方的支付秘钥
$payload['charset'] = "utf-8"; // 请求使用的编码格式
$payload['method'] = "tp.trade.create"; // 接口名称
$payload['timestamp'] = time(); // 发送请求的时间
$biz_content = ["out_order_no" => $data['out_trade_no'], //商户订单号"uid" => $data['openid'], //唯一标识用户open_id"merchant_id" => $tt_merchant_id, //头条支付分配给业务方的商户号"total_amount" => $data['fee'] * 100, //金额,分为单位,应传整型"currency" => "CNY", //币种"subject" => $data['subject'], //商户订单名称"body" => $data['body'], //商户订单详情"trade_time" => time(), //下单时间戳"valid_time" => "60", //订单有效时间 单位:秒"notify_url" => $website . '/payment/toutiao/notify.php', //服务器异步通知http地址"risk_info" => json_encode(['ip' => $risk_info]), //用户的真实ip
]; // 请求参数的集合 json
$payload['biz_content'] = json_encode($biz_content);
$payload['sign_type'] = "MD5"; //头条采用的是MD5加密
$payload['format'] = "json";
$payload['version'] = "1.0";
$stringToBeSigned = $this->getSignContent($payload , $payload['charset'] , $app_secret); //这里写了一个签名的方法
$payload["sign"] = md5($stringToBeSigned);$url = ""; // 请求地址正式环境
$result = ihttp_post($url, $payload);
$result = json_decode($result['content'], true);
$resultCode = $result['response']['code'];
if (!empty($resultCode) && $resultCode == 10000) {//后面会针对这一块进行处理//这里需要拼装参数请求支付宝APP支付return ['error'=>0 , 'msg'=>'success' , 'res'=>$result];
} else {return ['error'=>-1 , 'msg'=>'failed' , 'res'=>$result];
}
当我们点击点单支付按钮的时候,将所需要的参数传至以上接口方法中进行处理,能够正常返回头条的trade_no。
返回结果如下:
接下来就是上述方法中的两个方法的调用,一个使用户IP地址的获取,一个是头条签名的处理方法。重点是后面一个。
先看用户IP地址的获取方法,如下:
/*** 获取用户真实IP* @return bool*/
public function getRealIp() {$ip = false;if (!empty($_SERVER["HTTP_CLIENT_IP"])) {$ip = $_SERVER["HTTP_CLIENT_IP"];}if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {$ips = explode(", ", $_SERVER['HTTP_X_FORWARDED_FOR']);if ($ip) {array_unshift($ips, $ip);$ip = FALSE;}for ($i = 0; $i < count($ips); $i++) {if (!eregi("^(10│172.16│192.168).", $ips[$i])) {$ip = $ips[$i];break;}}}return ($ip ? $ip : $_SERVER['REMOTE_ADDR']);
}
该方法不做过多描述,接下来是重点。
通过文档可以看出,我们要先对请求参数去除空值和不参与签名的字段,然后进行排序。代码如下:
/*** 签名处理* @param $params* @param $charset* @return string*/public function getSignContent($params , $charset,$app_secret) {ksort($params);$stringToBeSigned = "";$i = 0;foreach ($params as $k => $v) {if (false === $this->checkEmpty($v) && "@" != substr($v, 0, 1)) {// 转换成目标字符集$v = $this->characet($v, $charset);if ($i == 0) {$stringToBeSigned .= "$k" . "=" . "$v";} else {$stringToBeSigned .= "&" . "$k" . "=" . "$v";}$i++;}}$stringToBeSigned = $stringToBeSigned.$app_secret;unset ($k, $v);return $stringToBeSigned;}/*** 校验$value是否非空* @param $value* @return boolean;* if not set ,return true;* if is null , return true;**/public function checkEmpty($value) {if (!isset($value))return true;if ($value === null)return true;if (trim($value) === "")return true;return false;}/*** 转换字符集编码* @param $data* @param $targetCharset* @return string*/public function characet($data, $targetCharset) {if (!empty($data)) {$fileType = "UTF-8";if (strcasecmp($fileType, $targetCharset) != 0) {$data = mb_convert_encoding($data, $targetCharset, $fileType);}}return $data;}
通过以上方法我们已经能够获取到正常的响应数据了。就像这样
{"response":{"code":"10000","msg":"Success","trade_no":"1004130000127421"},"sign":"ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
}
但是,接下来问题来了。我们需要再次的拼装参数,去请求支付宝APP支付。然后将数据返回给前端,通过前端拉起支付宝收银台。
但是这一块,官方文档写的很… … 以至于,本小白看的是一脸懵。
我在正确获取trade_no订单号的时候,做了以下处理:
关于这一块的参数,可以参考文档:.html
//在这里是要将数据返回给前端,通过前端拉起支付宝收银台$data2 = [];
$data2['trade_no'] = $result['response']['trade_no']; //刚刚获取到的trade_no订单号
$data2['app_id'] = $payload['app_id']; //头条支付分配给业务方的ID
$data2['sign_type'] = $payload['sign_type']; //头条采用的MD5
$data2['timestamp'] = $payload['timestamp']; //发送请求的时间
$data2['merchant_id'] = $biz_content['merchant_id']; //头条支付分配给业务方的商户号
$data2['uid'] = $biz_content['uid']; //唯一标识用户open_id(这个是头条的)
$data2['total_amount'] = $biz_content['total_amount']; //金额,分为单位,应传整型
$data2['params'] = json_encode(['url' => $this->aliapppaytest($data , $biz_content , $data2 , $website , $config)]); //这里是支付宝APP支付的请求参数,我在这里使用了支付宝的SDK$stringToBeSigned = $this->getSignContent($data2, $payload['charset'], $app_secret); //将以上参数再次进行签名处理
$data2["sign"] = md5($stringToBeSigned);$data2['pay_channel'] = "ALIPAY_NO_SIGN";
$data2["pay_type"] = "ALIPAY_APP";
$data2['method'] = 'tp.trade.confirm';
$data2['risk_info'] = $biz_content['risk_info'];
return ['error'=>0 , 'msg'=>'success' , 'res'=>$data2]; // 将这些参数,返回给前端处理就可以了
主要是参数params中的url处理
这里使用了支付宝的SDK,如下:
public function aliapppaytest($data , $biz_content , $data2 , $website , $config){require dirname(__FILE__) .'/../library/alipay/AopSdk.php';$aop = new \AopClient();$aop->gatewayUrl = ".do";$aop->appId = $config['ali_app_app_id']; //支付宝APPID$aop->rsaPrivateKey = $config['ali_app_rsa_pri_key']; //支付宝秘钥$aop->format = "json";$aop->charset = "UTF-8";$aop->signType = "RSA2";$aop->alipayrsaPublicKey = $config['ali_app_pay_rsa_pub_key']; //支付宝公钥//实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay$request = new AlipayTradeAppPayRequest(); //SDK已经封装掉了公共参数,这里只需要传入业务参数$data2 = ['body' => $data['body'],'subject' => $data['subject'],'out_trade_no' => $data2['trade_no'],'timeout_express' => '30m','total_amount' => $data['fee'],'product_code' => 'QUICK_MSECURITY_PAY',];$postdata = json_encode($data2);$request->setNotifyUrl($website . '/payment/toutiao/notify.php'); //服务器异步通知http地址);$request->setBizContent($postdata);$response = $aop->sdkExecute($request); //这里和普通的接口调用不同,使用的是sdkExecute//htmlspecialchars是为了输出到页面时防止被浏览器将关键参数html转义,实际打印到日志以及http传输不会有这个问题
// return htmlspecialchars($str);//就是orderString 可以直接给客户端请求,无需再做处理。return $response;//就是orderString 可以直接给客户端请求,无需再做处理。}
到这里,整个支付阶段就已经结束了。此时,前端拉起支付宝收银台进行付款,支付成功之后,关于回调的处理,其实就是支付宝本身的回调处理了。直接$_POST接收数据,通过支付宝验签处理就可以了。详情,参考PHP 支付宝小程序 支付以及回调处理
关于唤起支付页面报错的问题,请参考支付宝APP支付:
ALI40247-自查方案:.php?tid=250&fid=60
ALI38173-排查方案:.php?tid=3546&fid=60
ALIN10146-自查方案:.php?tid=6918&fid=60
以上欢迎各位指点。
更多推荐
字节跳动(头条)小程序 支付,回调处理
发布评论