需要申请&开通的内容
1.字节跳动小程序支付(图 1-1)
字节跳动小程序开发者平台 : https://developer.toutiao.com/app/applist

图1-1
2.开通支付宝App支付,并签约:https://openhome.alipay.com/platform/appManage.htm

图1-2
业务流程
1.获取抖音预支付单号
$this->tt_config = config('toutiaopay'); //抖音配置信息
$total_amount = $pay_order->pay_price*100; //抖音是 “ 分 ”作为单位
$ip = \request()->getClientIp();
//业务数据
$biz_content = array(
'out_order_no' => $pay_order->out_trade_no, //商户唯一订单号
'uid' => $ext['tt_openid'], //抖音用户openid
'merchant_id' => $this->tt_config['merchant_id'],//字节跳动小程序支付中的商户id
'total_amount' => $total_amount,
'currency' => 'CNY',
'subject' => $ext['subject'],
'body' => $ext['body'],
'trade_time' => NOW_TIME,
'valid_time' => '60',
'notify_url' => $this->tt_config['notify_url'],
'risk_info' => json_encode(['ip'=>$ip]) // 输出示例: "{\"ip\":\"110.90.28.70\"}",
);
$biz_content = json_encode($biz_content);
//公共请求参数
$params['app_id'] = $this->tt_config['app_id'];
$params['biz_content'] = $biz_content;
$params['method'] = $this->tt_config['method']; //注意:此处固定值 tp.trade.create
$params['charset'] = $this->tt_config['charset'];
$params['sign_type'] = $this->tt_config['sign_type'];
//$params['format'] = $this->tt_config['format']; //注意:此参数文档为必填,但可以不填
$params['timestamp'] = NOW_TIME;
$params['version'] = $this->tt_config['version'];
//签名(k-v 数组排序,用 “&” 来拼接成链接,中间无符号和空格拼接上 secret, md5加密)
ksort($params);
$signstr = '';
foreach ($params as $key => $value) {
if ($value == '') {
continue;
}
$signstr .= $key . '=' . $value . '&';
}
$signstr = trim($signstr, '&');
$params['sign'] = md5($signstr.$this->tt_config['secret']);
ksort($params);
//请求服务端支付接口(这里用的是 GuzzleHttp 对象)
$res = json_decode($this->httpPost($this->baseUri,$params),true);
$res = $res['response'];
if($res['code'] == '10000'){
$tt_trade_no = $res['trade_no']; //头条预支付单号
}else{
return ErrCodeService::get(1000,'支付订单统一下单失败');
}
2.获取支付宝唤起APP支付链接
//实例化alipay SDK
$alipay_config = config('alipay');
$aop = new AopClient;
$aop->gatewayUrl = "https://openapi.alipay.com/gateway.do";
$aop->appId = $alipay_config['app_id'];
$aop->rsaPrivateKey = $alipay_config['private_key'];//'请填写开发者私钥去头去尾去回车,一行字符串';
$aop->format = "json";
$aop->charset = "UTF-8";
$aop->signType = "RSA2";
$aop->alipayrsaPublicKey = $alipay_config['ali_public_key'];//'请填写支付宝公钥,一行字符串';
//【方法一】实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
$request = new AlipayTradeAppPayRequest();
//SDK已经封装掉了公共参数,这里只需要传入业务参数
$body = $ext['body'];
$subject = $ext['subject'];
$out_trade_no = $pay_order->out_trade_no;
$total_amount = $pay_order->pay_price;//单位为元
$bizcontent = "{\"body\":\"$body\","
. "\"subject\": \"$subject\","
. "\"out_trade_no\": \"$out_trade_no\","
. "\"timeout_express\": \"30m\","
. "\"total_amount\": \"$total_amount\","
. "\"product_code\":\"QUICK_MSECURITY_PAY\""
. "}";
$request->setNotifyUrl($alipay_config['notify_url']);//商户外网可以访问的异步地址
$request->setBizContent($bizcontent);
//这里和普通的接口调用不同,使用的是sdkExecute
$response = $aop->sdkExecute($request);
return $response;
-----------------------------分割线---------------------------
//【方法二 只生成签名串,拼接链接】
$body = $ext['body'];
$subject = $ext['subject'];
$out_trade_no = $pay_order->out_trade_no;
$total_amount = $pay_order->pay_price;//单位为元
$ali_data['app_id'] = $alipay_config['app_id'];
$ali_data['method'] = 'alipay.trade.app.pay'; //固定写死
$ali_data['charset'] = 'utf-8'; //固定写死
$ali_data['sign_type'] = 'RSA2'; //固定写死
$ali_data['timestamp'] = date('Y-m-d H:i:s',NOW_TIME);
$ali_data['version'] = '1.0'; //固定写死
$ali_data['notify_url'] = $alipay_config['notify_url'];
$bizcontent = "{\"subject\": \"$subject\","
. "\"out_trade_no\": \"$out_trade_no\"," //注意这里为商户唯一订单号
. "\"timeout_express\": \"30m\"," //固定写死
. "\"total_amount\": \"$total_amount\","
. "\"product_code\":\"QUICK_MSECURITY_PAY\"" //固定写死
. "}";
$ali_data['biz_content'] = $bizcontent;
$ali_data['sign'] = $aop->rsaSign($ali_data,'RSA2'); //只调用加密方法
//注意拼接时候必须要 encode 下,返回url字符串
foreach ($ali_data as $key => $value) {
$urls .= $key.'='.urlencode($value).'&';
}
$urls = trim($urls,'&');
return $urls;
3.生成字节跳动小程序支付请求参数
$total_amount = $pay_order->pay_price*100; //分为单位
$res = [];
$res['app_id'] = $this->tt_config['app_id'];
$res['sign_type'] = $this->tt_config['sign_type'];
$res['uid'] = $ext['tt_openid'];
$res['total_amount'] = $total_amount;
$res['timestamp'] = (string)NOW_TIME;
$res['trade_no']= $tt_trade_no; //注意:这里为字节跳动小程序返回的预支付单号(上 1. 中获取)
$res['merchant_id']= $this->tt_config['merchant_id'];
$res['params'] = "{\"url\":\"$ali_url\"}";
ksort($res);
$sign_str = '';
foreach ($res as $k => $v) {
$sign_str .=$k.'='.$v.'&';
}
$sign_str = trim($sign_str,'&');
// echo $sign_str.$this->tt_config['secret'];exit;
$res['sign'] = md5($sign_str.$this->tt_config['secret']);
$res['method'] = 'tp.trade.confirm'; //注意:这里于获取预支付单号不同,固定为 tp.trade.confirm
$res['pay_channel'] = $this->tt_config['pay_channel'];
$res['pay_type'] = "ALIPAY_APP";
$res['risk_info']= \request()->getClientIp();
$res['url'] =$ali_url;
return $res;
4.小程序端代码
pay:function () {
//1.请求服务端获取支付调起参数
let task = tt.request({
url: app.globalData.url+'/activity-buy',
data: { //此处服务端参数自行定义
api_token:"xxx",
buy_type:"xxx",
pay_type:"xxx",
id:xx
},
dataType:'json',
method:'POST',
header: {
'content-type': 'application/json'
},
success (res) {
//2.根据服务端参数拼接唤起支付宝请求
var js_request = res.data.data.js_request;
tt.requestPayment({
data: {
app_id: js_request.app_id,
method: js_request.method,
sign: js_request.sign,
sign_type: js_request.sign_type,
timestamp: js_request.timestamp,
trade_no: js_request.trade_no,
merchant_id: js_request.merchant_id,
uid: js_request.uid,
total_amount: js_request.total_amount,
pay_channel: js_request.pay_channel,
pay_type: js_request.pay_type,
params: JSON.stringify({
url: js_request.url
}),
risk_info:"{'ip':'" + js_request.risk_info + "'}"
},
success (res) {
console.log(res);
},
fail (res) {
console.log(res);
}
})
},
fail (res) {
console.log(`request调用失败`);
}
});
},
5.会出现的问题
a.优先保证(1.中的获取抖音预支付单号)正常获得
b.可以唤起支付宝后报错【支付宝报错:系统繁忙,请稍后再试。(ALIN10146)】
b1.支付宝签名错误,请检查sign加密参数是否缺漏或者控制
b2.支付宝App支付签约未通过
b3.检查参数比如「应用私钥」是否正确配置