最近接手了一個做支付的外包項目,前臺和後臺都要做,所以打算就此幾個筆記。
申請
在接通支付之前,我們還要做一些事情
創建應用
簽約
這個簽約部分需要實名認證的。我下面是簽約完成的,添加你需要的功能,然後簽約認證等待
生成公鑰和私鑰
生成公私鑰的工具下載地址
按照我的箭頭步驟來,官網說最好用2048的,然後點擊”生成密鑰“,這個時候你生成的密鑰都會保存在RSA密鑰文件夾下面,如果你下次要用的話,就打開文件夾裏面找到應用公鑰2048或是應用私鑰2048。(這個工具其實你可以隨便生成,想生成就怎麼生成,只要你保證,你上傳到支付寶的公鑰和你服務器配置的公鑰相同,並且私鑰和公鑰是匹配的就行,工具裏面有個密鑰匹配選項,可以匹配測試一下對不對)
公鑰上傳到支付寶
然後我們繼續進開發者中心,找到自己新建的應用,選擇“應用信息選項卡”。
這個地方我已經設置過公鑰了,你們可以設置一下。然後配置基本就完成了。
代碼配置
android配置
代碼的地方可以看這個文檔,把權限、清單文件、混淆等都配置好,然後我們開車。
首先,下載一下官網給的demo,看第三方的demo還是比較好容易理解的,我們把androidDemo打開,主要來看PayDemoActivity.java這個類
PayDemoActivity.java 我們看支付這個方法,其他的不看了,沒有用
/**
* 支付寶支付業務
*
* @param v
*/
public void payV2(View v) {
/*
註釋了,這部分不要,對於本地測試支付相關的都不要
if (TextUtils.isEmpty(APPID) || (TextUtils.isEmpty(RSA2_PRIVATE) && TextUtils.isEmpty(RSA_PRIVATE))) {
new AlertDialog.Builder(this).setTitle("警告").setMessage("需要配置APPID | RSA_PRIVATE")
.setPositiveButton("確定", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialoginterface, int i) {
//
finish();
}
}).show();
return;
}
*/
/**
* 這裏只是爲了方便直接向商戶展示支付寶的整個支付流程;所以Demo中加簽過程直接放在客戶端完成;
* 真實App裏,privateKey等數據嚴禁放在客戶端,加簽過程務必要放在服務端完成(多麼體貼的一句話)
* 防止商戶私密數據泄露,造成不必要的資金損失,及面臨各種安全風險;
*
* orderInfo的獲取必須來自服務端;
*/
// boolean rsa2 = (RSA2_PRIVATE.length() > 0);
//Map<String, String> params = OrderInfoUtil2_0.buildOrderParamMap(APPID, rsa2);
//String orderParam = OrderInfoUtil2_0.buildOrderParam(params);
//String privateKey = rsa2 ? RSA2_PRIVATE : RSA_PRIVATE;
//String sign = OrderInfoUtil2_0.getSign(params, privateKey, rsa2);
//final String orderInfo = orderParam + "&" + sign;
/*
這個orderInfo我們還是需要的,支付寶要求我們這個orderInfo從後臺返回給我們,不要在前臺去生成,這個支付demo就是給我們演示了一下,這個orderInfo的生成,來完成支付。
所以,我們等會在這個地方去後臺拿orderInfo,然後再繼續接下來的流程。
android配置這個部分我們先暫停,先去後臺配置一些東西。
*/
final String orderInfo="";
Runnable payRunnable = new Runnable() {
@Override
public void run() {
PayTask alipay = new PayTask(PayDemoActivity.this);
Map<String, String> result = alipay.payV2(orderInfo, true);
Log.i("msp", result.toString());
Message msg = new Message();
msg.what = SDK_PAY_FLAG;
msg.obj = result;
mHandler.sendMessage(msg);
}
};
Thread payThread = new Thread(payRunnable);
payThread.start();
}
後端php配置
我後臺使用的是php,其實其他配置都是一樣的,主要看流程。
後端demo下載鏈接,然後我們要看看這個文檔php部分的配置,java .NET都是這樣的一個流程,配置好一些信息基本就ok了。
這個地方我遇到了一些坑,怪我自己沒注意,列出來
(一)、php版本最少要php5.5以上的開發環境
(二)、去php的安裝目錄下面找到php.ini,打開該文件找到extension=php_openssl.dll,去電前面的分號,然後重啓服務器,因爲支付寶用的是https去驗證返回的,所以,需要打開這個。
好了。來看服務端代碼。
這個是官網給的demo樣本,我們就跟着這個來
$aop = new AopClient;
$aop->gatewayUrl = "https://openapi.alipay.com/gateway.do";
$aop->appId = "app_id";
$aop->rsaPrivateKey = '請填寫開發者私鑰去頭去尾去回車,一行字符串' ;
$aop->format = "json";
$aop->charset = "UTF-8";
$aop->signType = "RSA2";
$aop->alipayrsaPublicKey = '請填寫支付寶公鑰,一行字符串';
//實例化具體API對應的request類,類名稱和接口名稱對應,當前調用接口名稱:alipay.trade.app.pay
$request = new AlipayTradeAppPayRequest();
//SDK已經封裝掉了公共參數,這裏只需要傳入業務參數
$bizcontent = "{\"body\":\"我是測試數據\","
. "\"subject\": \"App支付測試\","
. "\"out_trade_no\": \"20170125test01\","
. "\"timeout_express\": \"30m\","
. "\"total_amount\": \"0.01\","
. "\"product_code\":\"QUICK_MSECURITY_PAY\""
. "}";
$request->setNotifyUrl("商戶外網可以訪問的異步地址");
$request->setBizContent($bizcontent);
//這裏和普通的接口調用不同,使用的是sdkExecute
$response = $aop->sdkExecute($request);
//htmlspecialchars是爲了輸出到頁面時防止被瀏覽器將關鍵參數html轉義,實際打印到日誌以及http傳輸不會有這個問題
echo htmlspecialchars($response);//就是orderString 可以直接給客戶端請求,無需再做處理。
我們在支付寶demo中新建一個pay文件夾,然後裏面新建一個orderInfo.php文件。
如圖所示:
在新建的文件和文件夾中,我們將官網給出的代碼粘貼進去,然後require_once引入下面要使用的類。
還是看代碼舒服,看圖不對勁。
<?php
//這個地方要引入下面要用到的類
require_once '../aop/AopClient.php';
require_once '../aop/request/AlipayTradeAppPayRequest.php';
$aop = new AopClient;
//這裏是網關,下面這個網關是正式環境的,等會配置沙箱測試環境的時候會換一個網關
$aop->gatewayUrl = "https://openapi.alipay.com/gateway.do";
//填寫appid,在應用的頭上面有
$aop->appId = "app_id";//
//這個地方填寫私鑰,就是我們在上面用工具生成的私鑰,這個私鑰必須是和上傳到支付寶的公鑰匹配,不讓,支付寶訪問的時候會匹配錯誤
$aop->rsaPrivateKey = '請填寫開發者私鑰去頭去尾去回車,一行字符串' ;
$aop->format = "json";
$aop->charset = "UTF-8";
$aop->signType = "RSA2";
//這個地方的公鑰也是一樣,必須是上傳到支付寶的那個公鑰要一樣
$aop->alipayrsaPublicKey = '請填寫支付寶公鑰,一行字符串';
//實例化具體API對應的request類,類名稱和接口名稱對應,當前調用接口名稱:alipay.trade.app.pay
$request = new AlipayTradeAppPayRequest();
//SDK已經封裝掉了公共參數,這裏只需要傳入業務參數
$bizcontent = "{\"body\":\"我是測試數據\","//這個地方寫一些參數,在彈出支付的時候會顯示
. "\"subject\": \"App支付測試\","//這個可以進行支付的一些描述
. "\"out_trade_no\": \"20170125test01\","//訂單號,必須是唯一的,等會我會給一個生成訂單號的函數,
. "\"timeout_express\": \"30m\","
. "\"total_amount\": \"0.01\","
. "\"product_code\":\"QUICK_MSECURITY_PAY\""
. "}";
$request->setNotifyUrl("商戶外網可以訪問的異步地址");
$request->setBizContent($bizcontent);
//這裏和普通的接口調用不同,使用的是sdkExecute
$response = $aop->sdkExecute($request);
//htmlspecialchars是爲了輸出到頁面時防止被瀏覽器將關鍵參數html轉義,實際打印到日誌以及http傳輸不會有這個問題
echo htmlspecialchars($response);//就是orderString 可以直接給客戶端請求,無需再做處理。
插入一個小東西,訂單號生成代碼
function order()
{
$yCode = array('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J');
$orderSn = $yCode[intval(date('Y')) - 2011] . strtoupper(dechex(date('m'))) . date('d') . substr(time(), -5) . substr(microtime(), 2, 5) . sprintf('%02d', rand(0, 99));
return $orderSn;
}
填寫完整後,我們來測試一下,訪問這個鏈接,看看返回的是啥
app_id=2016080601711375&biz_content=%7B%22body%22%3A%22%E6%88%91%E6%98%AF%E6%B5%8B%E8%AF%95%E6%95%B0%E6%8D%AE%22%2C%22subject%22%3A+%22App%E6%94%AF%E4%BB%98%E6%B5%8B%E8%AF%95%22%2C%22out_trade_no%22%3A+%2220170125test01%22%2C%22timeout_express%22%3A+%2230m%22%2C%22total_amount%22%3A+%220.01%22%2C%22product_code%22%3A%22QUICK_MSECURITY_PAY%22%7D&charset=UTF-8&format=json&method=alipay.trade.app.pay¬ify_url=http%3A%2F%2Fwww.inbandian.com%2F&sign_type=RSA2×tamp=2017-04-07+20%3A13%3A33&version=1.0&sign=UYy7Kk7Jz4ZnSLa1vwasEv0MRcMMzt2dcfdXU9hWCkc0HyqjzdsU6c97ANp062XjI0YogqCXhTBLAEScnwj%2FkkNEjqeLfPn3Gaave0AExEIIruKclX1lXpMMVhotcwx%2FDLgsQvtRlyVShEOOBCib8AvXVPknNb4CEzsOg7R4Ee7RN8TBjZ46PdO786U9hbMuRDdgvn753j4yoTruUj2YGpl1SeauUeOsTFDbrHKbEFWxU%2BtrbrgeJreps0vZ2hcNBLkOag9rmx4gTWKGxV0eoQB%2FpcJjBsKLfkViWo9vs1Nxnfwso%2FxQk%2FRv37zftvRVgRPqHUXnZHveUXoBqbQm2g%3D%3D
相信用android端支付的,用android端直接進行支付的打印出的訂單信息的格式肯定是和這個一樣的是吧,那就對了。
好了,鏈接也測試通過了,那我們只需要通過android端,每次支付的時候,通過這個接口去獲取訂單信息,然後將這個信息通過支付寶API的
Map<String, String> result = alipay.payV2(orderInfo, true);
就可以進行支付了,下面我給出簡潔後的android端代碼。
public class PayDemoActivity extends FragmentActivity {
private static final int SDK_PAY_FLAG = 1;
private static final int SDK_AUTH_FLAG = 2;
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
@SuppressWarnings("unused")
public void handleMessage(Message msg) {
switch (msg.what) {
case SDK_PAY_FLAG: {
@SuppressWarnings("unchecked")
PayResult payResult = new PayResult((Map<String, String>) msg.obj);
/**
對於支付結果,請商戶依賴服務端的異步通知結果。同步通知結果,僅作爲支付結束的通知。
*/
String resultInfo = payResult.getResult();// 同步返回需要驗證的信息
String resultStatus = payResult.getResultStatus();
// 判斷resultStatus 爲9000則代表支付成功
if (TextUtils.equals(resultStatus, "9000")) {
// 該筆訂單是否真實支付成功,需要依賴服務端的異步通知。
Toast.makeText(PayDemoActivity.this, "支付成功", Toast.LENGTH_SHORT).show();
} else {
// 該筆訂單真實的支付結果,需要依賴服務端的異步通知。
Toast.makeText(PayDemoActivity.this, "支付失敗", Toast.LENGTH_SHORT).show();
}
break;
}
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.pay_main);
}
/**
* 支付寶支付業務
*
* @param v
*/
public void payV2(View v) {
Runnable payRunnable = new Runnable() {
@Override
public void run() {
URL url = null;
try {
url = new URL("orderInfo.php的url地址");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String orderInfo = "";
String len;
while ((len = br.readLine()) != null) {
orderInfo += len;
}
Log.i("orderInfo", orderInfo);//打印一下看看對不對
PayTask alipay = new PayTask(PayDemoActivity.this);
Map<String, String> result = alipay.payV2(orderInfo, true);
Log.i("msp", result.toString());
Message msg = new Message();
msg.what = SDK_PAY_FLAG;
msg.obj = result;
mHandler.sendMessage(msg);
}catch(Exception e){
e.printStackTrace();
}
}
};
Thread payThread = new Thread(payRunnable);
payThread.start();
}
}
代碼還是挺少的,就這樣前端和後端打通了,接下來支付成功異步通知服務器什麼的,就可以發揮大家自己的想法啦(反正我已經做好了,棒棒的,哈哈)。
大家在做測試的時候一想,每次都要我用0.01去測試數據,未免有點坑啊,心疼我的錢啊,剛開始不懂的時候我就充值了好幾次,肉疼。
好了,下面給出沙箱測試的步驟,大致意思就是,支付寶會提供給你一個沙箱測試的賬號的密碼,而且還有指定的沙箱版支付寶APP,我們去下載一下這個支付寶APP,然後登陸支付寶給我們的賬號,有買家和商家的賬號都有,然後測試的時候,會喚起我們的這個沙箱版的支付寶,玩起來和真支付寶是一樣的。
沙箱版支付寶測試
既然是拿來測試用的,免不了配置,沙箱版的配置和之前的應用配置是一模一樣的。來看步驟。
配置
首先還是進開發者中心,如圖,選擇沙箱應用。
和之前的配置是不是又那麼點像呢,哈哈,沙箱版的支付寶網關和真實環境是不一樣的,我們等會去替換他,還有appid,下面的密鑰配置和之前步驟一樣,打開密鑰工具,隨機生成一個2048的密鑰,將公鑰張貼進去。
我們繼續往下滑動,下面會有一個沙箱版的app的二維碼,我們掃描一下,把他下載下來。
好了,我們現在服務端文件夾中的pay文件夾重新新建一個sandboxOrderInfo.php
<?php
//這個地方要引入下面要用到的類
require_once '../aop/AopClient.php';
require_once '../aop/request/AlipayTradeAppPayRequest.php';
$aop = new AopClient;
//這裏是網關,填寫沙箱版裏面給的網關
$aop->gatewayUrl = "https://openapi.alipay.com/gateway.do";
//填寫appid,填寫沙箱版裏面給的appid
$aop->appId = "app_id";//
//這個地方填寫私鑰,就是我們在上面用工具生成的私鑰,這個私鑰必須是和上傳到支付寶的公鑰匹配,不讓,支付寶訪問的時候會匹配錯誤
$aop->rsaPrivateKey = '請填寫開發者私鑰去頭去尾去回車,一行字符串' ;
$aop->format = "json";
$aop->charset = "UTF-8";
$aop->signType = "RSA2";
//這個地方的公鑰也是一樣,必須是上傳到支付寶的那個公鑰要一樣
$aop->alipayrsaPublicKey = '請填寫支付寶公鑰,一行字符串';
//實例化具體API對應的request類,類名稱和接口名稱對應,當前調用接口名稱:alipay.trade.app.pay
$request = new AlipayTradeAppPayRequest();
//SDK已經封裝掉了公共參數,這裏只需要傳入業務參數
$bizcontent = "{\"body\":\"我是測試數據\","//這個地方寫一些參數,在彈出支付的時候會顯示
. "\"subject\": \"App支付測試\","//這個可以進行支付的一些描述
. "\"out_trade_no\": \"20170125test01\","//訂單號,必須是唯一的,等會我會給一個生成訂單號的函數,
. "\"timeout_express\": \"30m\","
. "\"total_amount\": \"0.01\","
. "\"product_code\":\"QUICK_MSECURITY_PAY\""
. "}";
$request->setNotifyUrl("商戶外網可以訪問的異步地址");//這個異步地址是自己在開發者中心我的應用中配置的
$request->setBizContent($bizcontent);
//這裏和普通的接口調用不同,使用的是sdkExecute
$response = $aop->sdkExecute($request);
//htmlspecialchars是爲了輸出到頁面時防止被瀏覽器將關鍵參數html轉義,實際打印到日誌以及http傳輸不會有這個問題
echo htmlspecialchars($response);//就是orderString 可以直接給客戶端請求,無需再做處理。
此處和之前只是環境的一樣,只需要修改app_id,網關,和對應的公鑰和私鑰就行了。好了,服務端的又配置好了。
我們來打開開發者中心的“沙箱賬號”。如圖
沙箱會給我們一個商家的和買家的賬號,便於我們測試用,看到充值那個選項沒,真的好像是我的真實賬號啊,想要多少就自己填,好爽。然後我們用這個賬號登陸我們的沙箱版APP,登陸成功我們就不需要去管他了,就當他是我們真是的支付寶賬號。
然後回到我們的android客戶端,我們在onCreate初始化的時候加上這麼一句代碼,告訴支付寶,我們這是處於沙箱環境。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.pay_main);
EnvUtils.setEnv(EnvUtils.EnvEnum.SANDBOX);
}
然後我們將訪問獲取訂單信息的url改成sandboxOrderInfo.php,然後進行訪問獲取,去支付,OK,大功告成。想怎麼支付就怎麼支付,好爽。
前端到後端結合還是挺簡單的,跟着這個思想套路走,java和ASP都是一樣的。
我要下樓去吃全套酸奶去了,味道啊!