支付宝手机网站支付实战踩坑
準(zhǔn)備
需求
需求就是最簡單的對接支付寶支付接口
方案心路歷程
實(shí)操
支付寶后臺配置
登錄支付寶后臺—進(jìn)入后臺—點(diǎn)擊能力管理—查看已獲取能力(手機(jī)網(wǎng)站支付)–!沒有的話可以點(diǎn)擊獲取更多能力申請—然后點(diǎn)擊管理進(jìn)入
綁定應(yīng)用—保存好appid
接著點(diǎn)擊密鑰管理—進(jìn)入賬戶中心—點(diǎn)擊開放平臺密鑰—設(shè)置接口加簽方式
下載支付寶開發(fā)工具
生成密鑰保存好應(yīng)用私鑰公鑰—復(fù)制應(yīng)用鑰在剛剛的加簽管理彈窗粘貼—保存設(shè)置—然后就會彈出支付寶公鑰—復(fù)制保存
點(diǎn)擊開發(fā)工具密鑰—粘貼應(yīng)用公鑰—設(shè)置本地ip與生產(chǎn)環(huán)境ip白名單
到這里支付寶后臺的配置就完成了
編碼
下載SDK—放入項(xiàng)目中
這里我們使用的是laravel框架為例
直接附上demo code
可以同時(shí)參考支付寶接口文檔
<?php namespace App\Services\Support\Ali; require_once 'aop/AopClient.php'; // 引入SDK類 require_once 'aop/request/AlipayTradeWapPayRequest.php'; // 引入SDK類 use Illuminate\Support\Facades\Log; class Alipay {public function __construct(){// 配置SDK$this->baseUrl = config('ali.alipay.baseUrl');$this->appid = config('ali.alipay.appid');$this->privateKey = config('ali.alipay.privateKey');$this->publicKey = config('ali.alipay.publicKey');$this->aliPublicKey = config('ali.alipay.aliPublicKey');$this->sign_type = "RSA2";$this->charset = "utf-8";}public function wapPay($options){$aop = new \AopClient();$aop->gatewayUrl = $this->baseUrl . 'gateway.do';$aop->appId = $this->appid;$aop->rsaPrivateKey = $this->privateKey;$aop->alipayrsaPublicKey = $this->aliPublicKey;$aop->apiVersion = '1.0';$aop->signType = 'RSA2';$aop->postCharset = 'utf-8';$aop->format = 'json';$request = new \AlipayTradeWapPayRequest();// 配置支付信息$request->setReturnUrl($options['returnUrl']);$request->setNotifyUrl($options['notifyUrl']);$content = ["subject" => $options['subject'],"out_trade_no" => $options['outTradeNo'],"total_amount" => $options['totalAmount'],"quit_url" => $options['quitUrl'],"product_code" => "QUICK_WAP_PAY",];$bizContent = json_encode($content);$request->setBizContent($bizContent);$result = $aop->pageExecute($request);// 創(chuàng)建支付訂單,返回支付頁面的Form// Log::info("Alipay->wapPay():result: $result");return $result;} }服務(wù)入口
<?php namespace App\Services; use App\Services\Support\Ali\Alipay; class Ali {public function __construct(){}public function alipay(){return new Alipay();} }路由入口
<?php namespace App\Http\Controllers; use App\Services\Ali; use App\Utils\Common\DataHandle; class TestController extends Controller {public function test(){$systemSlug = 'test';return response()->json((new Ali())->alipay()->wapPay(["subject" => '支付測試',"totalAmount" => 0.01,'outTradeNo' => sprintf("%s_%s_%s", $systemSlug, time(), DataHandle::strRandom(17, 'key')), // 自定義'timestamp' => date('Y-m-d H:i:s'),'quitUrl' => config('ali.alipay.quitUrl'),'returnUrl' => config('ali.alipay.returnUrl'),'notifyUrl' => config('ali.alipay.notifyUrl') . 100,]));} }SDK返回的form內(nèi)容
<form id='alipaysubmit' name='alipaysubmit' action='https://openapi.alipay.com/gateway.do?charset=utf-8' method='POST'><input type='hidden' name='biz_content' value='{\"subject\":\"test\",\"out_trade_no\":\"test_1623329685_lpfbl1dan16dd5xw2\",\"total_amount\":0.01,\"quit_url\":\"xxx\",\"product_code\":\"QUICK_WAP_PAY\"}'/><input type='hidden' name='app_id' value='xxx'/><input type='hidden' name='version' value='1.0'/><input type='hidden' name='format' value='json'/><input type='hidden' name='sign_type' value='RSA2'/><input type='hidden' name='method' value='alipay.trade.wap.pay'/><input type='hidden' name='timestamp' value='2021-06-10 20:54:45'/><input type='hidden' name='alipay_sdk' value='alipay-sdk-PHP-4.11.14.ALL'/><input type='hidden' name='notify_url' value='xxx'/><input type='hidden' name='return_url' value='xxx'/><input type='hidden' name='charset' value='utf-8'/><input type='hidden' name='sign' value='xxx'/><input type='submit' value='ok' style='display:none;''></form><script>document.forms['alipaysubmit'].submit();</script>前端處理form,我這里使用的是uniapp,這個(gè)東西也踩了我很多坑
this.$http.post({path: "test",dontShowToast: true,errData: true,data: data, }).then((res) => {if (this.alipay) {//將接口返回的Form表單顯示到頁面document.querySelector("body").innerHTML = res.data;//調(diào)用submit 方法document.forms[0].submit();}console.log(res.message);this.submitSuccess(); })效果
異步處理結(jié)果
<?php namespace App\Http\Controllers\Api; use App\Http\Controllers\Api\Controller; use App\Payorder; use App\Services\Ali; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; class NotifyController extends Controller {public function payCall($payType){try {switch ($payType) {case 100:// 支付寶手機(jī)網(wǎng)站支付$data = $_POST;// 異步回調(diào)驗(yàn)簽if (!(new Ali())->alipay()->signCheck($data)) {Log::error("fails for notify, 驗(yàn)簽失敗 data: " . json_encode($data));return $this->rsp(403, []);}$orderId = $data['out_trade_no'];$statusMapState = ['WAIT_BUYER_PAY' => 0, // 交易創(chuàng)建,等待買家付款。'TRADE_CLOSED' => -1, // 在指定時(shí)間段內(nèi)未支付時(shí)關(guān)閉的交易或在交易完成全額退款成功時(shí)關(guān)閉的交易。'TRADE_SUCCESS' => 1, // 商戶簽約的產(chǎn)品支持退款功能的前提下,買家付款成功。'TRADE_FINISHED' => 2, // 商戶簽約的產(chǎn)品不支持退款功能的前提下,買家付款成功;或者,商戶簽約的產(chǎn)品支持退款功 能的前提下,交易已經(jīng)成功并且已經(jīng)超過可退款期限。];$state = $statusMapState[$data['trade_status']];break;default:return $this->rsp(403, []);}} catch (\Exception $e) {Log::error("fails for notify, $e");return $this->rsp(403, []);}DB::beginTransaction();// 支付完成操作$payorder = Payorder::where(['order_id' => $orderId, 'state' => 0])->first();if (!$payorder) {DB::rollBack();return $this->rsp(400, [], '參數(shù)錯(cuò)誤');}$payorder->notifyData = $data;$payorder->state = $state;$payorder->save();DB::commit();return $this->rsp(0, []);} }這個(gè)demo中只是簡單的記錄支付結(jié)果我配置的異步回調(diào)url是https://xxx/api/notify/payCall/{paytype}
踩坑
laravel與aop沖突
laravel中引入aop會導(dǎo)致框架自定義函數(shù)與SDK沖突,這里我們需要手動(dòng)修改SDK源碼
將原本的encrypt和decrypt函數(shù)名修改成自定義的名字,我這里修改為alipayEncrpt和alipayDecrypt
然后把使用到這兩個(gè)函數(shù)的源碼也一并修改
我這里是已經(jīng)修改好之后的
支付完成同步回調(diào)失效
在使用SDK中的alipay.trade.wap.pay接口時(shí)遇到了同步回調(diào)失效問題
跟蹤問題
在上面的代碼中是已經(jīng)設(shè)置了同步回調(diào)和異步回調(diào)配置的,這里忍不住吐槽一下支付寶文檔,找不到在哪里可以配置這兩個(gè)參數(shù)
這是支付寶的請求實(shí)例代碼
$aop = new AopClient (); $aop->gatewayUrl = 'https://openapi.alipay.com/gateway.do'; $aop->appId = 'your app_id'; $aop->rsaPrivateKey = '請?zhí)顚戦_發(fā)者私鑰去頭去尾去回車,一行字符串'; $aop->alipayrsaPublicKey='請?zhí)顚懼Ц秾毠€,一行字符串'; $aop->apiVersion = '1.0'; $aop->signType = 'RSA2'; $aop->postCharset='GBK'; $aop->format='json'; $request = new AlipayTradeWapPayRequest (); $request->setBizContent("{" . "\"body\":\"Iphone6 16G\"," . "\"subject\":\"大樂透\"," . "\"out_trade_no\":\"70501111111S001111119\"," . "\"timeout_express\":\"90m\"," . "\"time_expire\":\"2016-12-31 10:05\"," . "\"total_amount\":9.00," . "\"auth_token\":\"appopenBb64d181d0146481ab6a762c00714cC27\"," . "\"goods_type\":\"0\"," . "\"quit_url\":\"http://www.taobao.com/product/113714.html\"," . "\"passback_params\":\"merchantBizType%3d3C%26merchantBizNo%3d2016010101111\"," . "\"product_code\":\"QUICK_WAP_PAY\"," . "\"promo_params\":\"{\\\"storeIdType\\\":\\\"1\\\"}\"," . "\"extend_params\":{" . "\"sys_service_provider_id\":\"2088511833207846\"," . "\"hb_fq_num\":\"3\"," . "\"hb_fq_seller_percent\":\"100\"," . "\"industry_reflux_info\":\"{\\\\\\\"scene_code\\\\\\\":\\\\\\\"metro_tradeorder\\\\\\\",\\\\\\\"channel\\\\\\\":\\\\\\\"xxxx\\\\\\\",\\\\\\\"scene_data\\\\\\\":{\\\\\\\"asset_name\\\\\\\":\\\\\\\"ALIPAY\\\\\\\"}}\"," . "\"card_type\":\"S0JP0000\"," . "\"specified_seller_name\":\"XXX的跨境小鋪\"" . " }," . "\"merchant_order_no\":\"20161008001\"," . "\"enable_pay_channels\":\"pcredit,moneyFund,debitCardExpress\"," . "\"disable_pay_channels\":\"pcredit,moneyFund,debitCardExpress\"," . "\"store_id\":\"NJ_001\"," . " \"goods_detail\":[{" . " \"goods_id\":\"apple-01\"," . "\"alipay_goods_id\":\"20010001\"," . "\"goods_name\":\"ipad\"," . "\"quantity\":1," . "\"price\":2000," . "\"goods_category\":\"34543238\"," . "\"categories_tree\":\"124868003|126232002|126252004\"," . "\"body\":\"特價(jià)手機(jī)\"," . "\"show_url\":\"http://www.alipay.com/xxx.jpg\"" . " }]," . "\"specified_channel\":\"pcredit\"," . "\"business_params\":\"{\\\"data\\\":\\\"123\\\"}\"," . "\"ext_user_info\":{" . "\"name\":\"李明\"," . "\"mobile\":\"16587658765\"," . "\"cert_type\":\"IDENTITY_CARD\"," . "\"cert_no\":\"362334768769238881\"," . "\"min_age\":\"18\"," . "\"fix_buyer\":\"F\"," . "\"need_check_info\":\"F\"" . " }" . " }"); $result = $aop->pageExecute ( $request); $responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response"; $resultCode = $result->$responseNode->code; if(!empty($resultCode)&&$resultCode == 10000){ echo "成功"; } else { echo "失敗"; }不過通過查看AlipayTradeWapPayRequest我找到了這兩個(gè)函數(shù)
但是這反而讓我迷失自我了,同步回調(diào)url已經(jīng)設(shè)置好了,那么為什么還會失效呢??
百度了一圈,并沒有找到問題
emmm…
接著,終于發(fā)現(xiàn)問題關(guān)鍵點(diǎn),我習(xí)慣保留第三方對接的上下文,回來查看SDK給我返回的form
我發(fā)現(xiàn)SDK給我返回的form有notify_url但是并沒有return_url`這個(gè)input,上圖是修復(fù)問題后的返回結(jié)果
于是我又回去看SDK源碼
這里附上完整的AopClient類中的pageExecute函數(shù)
<?phprequire_once 'AopEncrypt.php'; require_once 'EncryptParseItem.php'; require_once 'EncryptResponseData.php'; require_once 'SignData.php';class AopClient {...public function pageExecute($request, $httpmethod = "POST", $appAuthToken = null){$this->setupCharsets($request);if (strcasecmp($this->fileCharset, $this->postCharset)) {// writeLog("本地文件字符集編碼與表單提交編碼不一致,請務(wù)必設(shè)置成一樣,屬性名分別為postCharset!");throw new Exception("文件編碼:[" . $this->fileCharset . "] 與表單提交編碼:[" . $this->postCharset . "]兩者不一致!");}$iv = null;if (!$this->checkEmpty($request->getApiVersion())) {$iv = $request->getApiVersion();} else {$iv = $this->apiVersion;}//組裝系統(tǒng)參數(shù)$sysParams["app_id"] = $this->appId;$sysParams["version"] = $iv;$sysParams["format"] = $this->format;$sysParams["sign_type"] = $this->signType;$sysParams["method"] = $request->getApiMethodName();$sysParams["timestamp"] = date("Y-m-d H:i:s");$sysParams["alipay_sdk"] = $this->alipaySdkVersion;if (!$this->checkEmpty($request->getTerminalType())) {$sysParams["terminal_type"] = $request->getTerminalType();}if (!$this->checkEmpty($request->getTerminalInfo())) {$sysParams["terminal_info"] = $request->getTerminalInfo();}if (!$this->checkEmpty($request->getProdCode())) {$sysParams["prod_code"] = $request->getProdCode();}if (!$this->checkEmpty($request->getNotifyUrl())) {$sysParams["notify_url"] = $request->getNotifyUrl();}$sysParams["charset"] = $this->postCharset;if (!$this->checkEmpty($appAuthToken)) {$sysParams["app_auth_token"] = $appAuthToken;}//獲取業(yè)務(wù)參數(shù)$apiParams = $request->getApiParas();if (method_exists($request, "getNeedEncrypt") && $request->getNeedEncrypt()) {$sysParams["encrypt_type"] = $this->encryptType;if ($this->checkEmpty($apiParams['biz_content'])) {throw new Exception(" api request Fail! The reason : encrypt request is not supperted!");}if ($this->checkEmpty($this->encryptKey) || $this->checkEmpty($this->encryptType)) {throw new Exception(" encryptType and encryptKey must not null! ");}if ("AES" != $this->encryptType) {throw new Exception("加密類型只支持AES");}// 執(zhí)行加密$enCryptContent = alipayEncrypt($apiParams['biz_content'], $this->encryptKey);$apiParams['biz_content'] = $enCryptContent;}//print_r($apiParams);$totalParams = array_merge($apiParams, $sysParams);//待簽名字符串$preSignStr = $this->getSignContent($totalParams);//簽名$totalParams["sign"] = $this->generateSign($totalParams, $this->signType);if ("GET" == strtoupper($httpmethod)) {//value做urlencode$preString = $this->getSignContentUrlencode($totalParams);//拼接GET請求串$requestUrl = $this->gatewayUrl . "?" . $preString;return $requestUrl;} else {//拼接表單字符串return $this->buildRequestForm($totalParams);}}... }看完,同志們發(fā)現(xiàn)什么了嗎???
有設(shè)置notify_url參數(shù),并沒有設(shè)置return_url的代碼,what??
恕我愚昧,我不知道這算不算bug,不過在我的demo里的確是同步回調(diào)失敗
最后,修改為以上代碼,解決問題
總結(jié)
以上是生活随笔為你收集整理的支付宝手机网站支付实战踩坑的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《士兵突击》之伍六一:最钢铁的男儿最柔软
- 下一篇: (转)80后生存法则