由于蘋果審核機制變化苹熏,除了JSPatch
等熱修復的應用受到影響外,另個影響較大的就是非法集成第三方支付SDK(尤其支付寶)
而審核被拒劳闹。但是由于你懂的
的原因,不想走IAP(In App Pay)
掌栅,所以當然想到了支付寶 WAP 支付
。完成 WAP 支付大概花了三天多時間码泛,但是有大概一天時間是在等簽約
猾封,所以為了讓大家和自己有需要的話快速集成,特意做一個總結噪珊。涉及 iOS(OC)
和JS(HTML5)
以及PHP
晌缘,下面進入正題。
吐槽支付寶開放平臺
之前我一直覺得只有微信的開發(fā)平臺網站比較容易搞混痢站,(一個是微信開放平臺磷箕,另個是微信公眾平臺),但是接觸支付寶后阵难,深深陷入各個跳轉和新舊文檔難以自拔(我記得之前沒這么亂啊岳枷,我知道網站迭代更新向后兼容不易,但是能不能稍微克制一下呜叫?空繁?)
先扔出個WAP支付開發(fā)文檔的鏈接出來,大家最好直接輸入鏈接怀偷,搜索引擎慎用家厌。(你能明白花了很長時間研究文檔最后發(fā)現是老版本的痛苦嗎)
WAP支付開發(fā)文檔
開發(fā)步驟
準備工作
- 注冊商家等都不說了,很簡單
- 雖然之前已經簽約過 APP 支付功能椎工,但是做手機網站支付的話還得繼續(xù)簽約手機網站支付功能,注意
APPID
是不一樣的蜀踏。簽約網址在這里簽約申請维蒙,開始的時候我以為我們運營同事已經幫忙申請好,但是最后在調支付寶接口的時候老是報錯ISV權限不足
果覆,經過檢查才發(fā)現是沒有簽約手機網站支付颅痊,提交申請審核時間是一個工作日。所以大家記得提前申請局待。 - 簽約成功后斑响,由于支付寶使用
RSA
數據加密方式菱属,非對稱加密,所以在本機可以openssl
生成應用私鑰和應用公鑰(統(tǒng)稱密鑰)舰罚,應用私鑰在自己的代碼中加密數據的時候會用到纽门,應用公鑰配置到支付寶后臺產生支付寶公鑰進行數據解密士嚎。注意下圖陶耍,配置手機網站支付的時候選擇左邊的平臺開放密鑰
,然后找到對應的APPID
產品進行公鑰配置买乃。我一開始失誤配置成了下面的mapi網關產品密鑰
饲漾,導致報錯驗簽失敗
蝙搔。
WX20170322-200717.png
接下來修改 OC
代碼調用支付寶SDK支付換成打開HTML
網頁,在網頁里面完成支付功能考传。
注意后面的queryString
吃型,由于支付網頁需要UID,考慮到方便快捷僚楞,沒有選擇用 WebViewJSBridge
進行數據交互勤晚,而是直接拼在鏈接后面。
// 阿里 wap 支付
- (void)wapPay {
ZKSafariViewController *vc = [ZKSafariViewController new];
NSString *oriUrl = @"...";
NSString *queryStr = [NSString stringWithFormat:@"?uid=%@&fee=%@", _loginUser.uid, _total_fee];
NSString *urlStr = [oriUrl stringByAppendingString:queryStr];
vc.url = urlStr;
[_applicationContext.navigationController pushViewController:vc animated:YES];
}
編寫HTML
頁面镜硕,提交數據到PHP
后端运翼。(前端代碼)
代碼如下:
<form name="alipayment" id="alipayment" action='...AliPay/CreateWapPay' method=post>
// ... 一些 input 標簽,提交到服務器
</form>
<script type="text/javascript">
function GetDateNow() {
let totalFee = getQueryStringArgs().total_fee,
uid = getQueryStringArgs().uid;
// ...
document.getElementById("uid").value = uid;
document.getElementById("total_fee").value = totalFee;
// ...
}
GetDateNow();
document.forms['alipayment'].submit();
</script>
集成 支付寶 PHP SDK 詳細步驟(后端代碼)
- 下載PHP DEMO
- 將SDK拖入項目文件兴枯,配置
config.php
文件血淌,如下
$config = array (
'app_id' => "...",
//商戶私鑰,您的原始格式RSA私鑰
'merchant_private_key' => "...",
//異步通知地址
'notify_url' => "http://工程公網訪問地址/alipay.trade.wap.pay-PHP-UTF-8/notify_url.php",
//同步跳轉
'return_url' => "http://mitsein.com/alipay.trade.wap.pay-PHP-UTF-8/return_url.php",
//編碼格式
'charset' => "UTF-8",
//簽名方式
'sign_type'=>"RSA",
//支付寶網關
'gatewayUrl' => "https://openapi.alipay.com/gateway.do",
//支付寶公鑰,查看地址:https://openhome.alipay.com/platform/keyManage.htm 對應APPID下的支付寶公鑰财剖。
'alipay_public_key' => "...",
);
- 組裝系統(tǒng)參數
$sysParams
(app_id
,return_url
,nofify_url
,sign_type
等)悠夯,獲取業(yè)務參數$apiParams
(放在biz_content
字段中)并將其加密如下
$enCryptContent = encrypt($apiParams['biz_content'], $this->encryptKey);
$apiParams['biz_content'] = $enCryptContent;
```
將系統(tǒng)參數和業(yè)務參數組裝在一起進行簽名
```
$totalParams = array_merge($apiParams, $sysParams);
//待簽名字符串
$preSignStr = $this->getSignContent($totalParams);
//簽名
$totalParams["sign"] = $this->generateSign($totalParams, $this->signType);
- 然后到了關鍵的地方,利用
total_params
拼接表單字符串躺坟,方法如下
/**
* 建立請求沦补,以表單HTML形式構造(默認)
* @param $para_temp 請求參數數組
* @return 提交表單HTML文本
*/
protected function buildRequestForm($para_temp) {
$sHtml = "<form id='alipaysubmit' name='alipaysubmit' action='".$this->gatewayUrl."?charset=".trim($this->postCharset)."' method='POST'>";
while (list ($key, $val) = each ($para_temp)) {
if (false === $this->checkEmpty($val)) {
//$val = $this->characet($val, $this->postCharset);
$val = str_replace("'","'",$val);
//$val = str_replace("\"",""",$val);
$sHtml.= "<input type='hidden' name='".$key."' value='".$val."'/>";
}
}
//submit按鈕控件請不要含有name屬性
$sHtml = $sHtml."<input type='submit' value='ok' style='display:none;''></form>";
$sHtml = $sHtml."<script>document.forms['alipaysubmit'].submit();</script>";
return $sHtml;
}
- 執(zhí)行第4步生成的表單html代碼被類
AlipayTradeService
進行echo
到前端頁面上,包含JS自動提交腳本咪橙,所以就直接調起了支付寶支付夕膀。為了讓大家更加明白,生成的h5代碼形式如下:
<form id='alipaysubmit' name='alipaysubmit' action='https://openapi.alipay.com/gateway.do?charset=UTF-8' method='POST'>
<input type='hidden' name='biz_content' value='{"productCode":"QUICK_WAP_PAY","body":null,"subject":null,"out_trade_no":"1234","total_amount":"100","timeout_express":"1m"}'/>
<input type='hidden' name='app_id' value='2088711989778894'/>
<input type='hidden' name='version' value='1.0'/>
<input type='hidden' name='format' value='json'/>
<input type='hidden' name='sign_type' value='RSA'/>
<input type='hidden' name='method' value='alipay.trade.wap.pay'/>
<input type='hidden' name='timestamp' value='2017-03-20 17:16:15'/>
<input type='hidden' name='alipay_sdk' value='alipay-sdk-php-20161101'/>
<input type='hidden' name='notify_url' value='http://.../notify_url.php'/>
<input type='hidden' name='return_url' value='http://.../return_url'/>
<input type='hidden' name='charset' value='UTF-8'/>
<input type='hidden' name='sign' value='vgU9ROZeES3fa6CPo5onwY2auGN7N6naNI8Wo5l2U/K6LPk2stkv0Cor5Dn57Xo83GefOnoPg5A/7dNLbZjTXioPaocrPg3LteDB/EV3zYHXUPsab7dPztW+7guQDbLXI1RtaEuPm85hjO2Cur5EmP3P3sAE1XVGYJHHtLJoAbKzm/I='/>
<input type='submit' value='ok' style='display:none;''>
</form>
<script>document.forms['alipaysubmit'].submit();</script>"
當上面的H5代碼被echo
到前端頁面美侦,會自動submit
表單信息产舞。繼續(xù)往下看
在 iOS 中實現代理進行跳轉(iOS 移動端代碼)
自動提交后,當手機有安裝支付寶的時候菠剩,在webView
中實現一個協議方法即可自動跳轉到支付寶客戶端易猫。如果沒有安裝,進入支付寶的H5收銀臺進行支付具壮。協議方法如下
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest: (NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSString* reqUrl = request.URL.absoluteString;
if ([reqUrl hasPrefix:@"alipays://"] || [reqUrl hasPrefix:@"alipay://"]) {
BOOL bSucc = [[UIApplication sharedApplication]openURL:request.URL];
if (!bSucc) {
// 未安裝支付寶 進入網頁支付
}
return NO;
}
return YES;
}
善后工作
- 當支付成功后准颓,會自動跳轉到
return_url
頁面哈蝇,這個頁面簡單展示一下支付成功信息即可。注意并不能作為交易成功的憑證攘已。 - 支付成功后炮赦,除了會進入上面提到的
return_url
,支付寶還會異步通知notify_url
贯被,并傳遞詳細的交易信息眼五,支付寶會根據上面?zhèn)魅氲漠惒酵ㄖ刂?code>notify_url通過POST
請求的形式將支付結果作為參數通知到商戶后臺系統(tǒng)。在這個接口中實現驗簽和支付成功的業(yè)務邏輯代碼彤灶。
上面提到的常用的支付寶網站羅列一下
- 新版開發(fā)文檔入口 https://openhome.alipay.com/developmentDocument.htm
- 查看是否已經簽約 https://app.alipay.com/market/productIndex.htm
- 開放平臺密鑰管理 https://openhome.alipay.com/platform/keyManage.htm
- 調用支付寶網關接口
https://openapi.alipay.com/gateway.do
https://doc.open.alipay.com/doc2/detail.htm?treeId=203&articleId=105463&docType=1
分享幾個這個項目中比較好用的函數
- 創(chuàng)建訂單號 很簡單看幼,月日時分秒 + 5位隨機數
/**
* Created by ZK on 17/3/22.
*/
// 創(chuàng)建訂單編號
function generateOrderID(){
var formatTime = function (date) {
var month = date.getMonth() + 1,
day = date.getDate(),
hour = date.getHours(),
minute = date.getMinutes(),
second = date.getSeconds();
return [month, day,hour, minute, second].map(formatNumber).join('') ;
};
function formatNumber(n) {
n = n.toString();
return n[1] ? n : '0' + n;
}
var nowDate = new Date(),
dateStr = formatTime(nowDate),
randomNum = Math.random()*1000000000,
oriStr = dateStr + randomNum,
orderID = oriStr.substr(0,15);
return orderID;
}
- 取得 QueryString 參數值
function getQueryStringArgs() {
var qs = (location.search.length > 0 ? location.search.substring(1) : ''),
args = {},
items = qs.length ? qs.split('&') : [],
item = null,
name = null,
value = null,
i = 0,
len = items.length;
for (i = 0; i < len; i ++) {
item = items[i].split('=');
name = decodeURIComponent(item[0]);
value = decodeURIComponent(item[1]);
if (name.length) {
args[name] = value;
}
}
return args;
}
- 另外一個比較好用的在
PHP
代碼中插入JS代碼實現彈出debug信息,如果服務器不支持write info
幌陕,這招很方便诵姜。
echo '<script type="text/javascript">alert("testMsg");</script>';
// 輸出變量內容
$name = 'dev_zk';
echo '<script type="text/javascript">alert('.$name.');</script>';
多分享幾個坑
- JS 腳本中的代碼最好別用
ES6
以上的版本,除非你用babel
自動轉換ES5
搏熄。我就栽坑了棚唆,寫的ES6腳本在iOSVersion <=9.0
的系統(tǒng)中全部失效報錯,但是在移動應用中測試 JS 的不方便你是知道的心例,找了好久才意識到這個問題宵凌。高估了WebKit
對JS的版本支持更新速度。 -
ISV權限不足
--- 沒有簽約對應功能止后。 -
驗簽失敗
--- 沒有在對應APPID設置正確公鑰瞎惫。