天天看點

Android用戶端和Java伺服器端內建支付寶

移動端內建支付,似乎是每個App都可能面臨的一件事。所有項目都在談盈利模式,而從C端擷取現金流是盈利中最重要的一個途徑之一。

目前大家主要采用微信支付和阿裡支付內建到自己的移動應用,雖然官方提供一些文檔和Demo,但是文檔的不完整性,資訊分散,內建的時候,依然會有很多困惑。本文從Android的Client內建,到伺服器內建對阿裡支付內建做介紹。

從網上找到的文章裡,大多數隻說了如何內建用戶端,但是很少提及伺服器。雖然如此,伺服器內建是一定要做的。如果僅憑客服端返的資料就更改訂單狀态是極為危險的,因為任何人都可以通過技術手段僞造資料,發資料給伺服器修改訂單狀态,會造成極大的經濟損失。并且強烈建議,訂單狀态的修改不要依賴用戶端,用戶端的傳回,可以作為校驗輔助。

下面是內建阿裡支付的資料互動流,可以在這裡找到:

https://doc.open.alipay.com/doc2/detail?treeId=59&articleId=103563&docType=1

Android用戶端和Java伺服器端內建支付寶

使用者在用戶端發起支付後,打開支付寶。需要解釋的一點是,無論是完成支付,還是按掉叉叉取消支付,阿裡支付的伺服器端都會給應用伺服器(商戶伺服器,“我們的伺服器”)同步一條資料,通知伺服器使用者的詳細支付結果。是以用戶端內建和伺服器內建都必不可少。

本文分别從Android用戶端和Java伺服器端內建,介紹內建阿裡支付的詳細過程。

其中,Android端支付,主要參考了官方Demo和博文:

http://my.oschina.net/daniels/blog/597356?fromerr=CxXSKO5S

伺服器端內建主要是在官方Demo基礎上自己實踐,伺服器端可運作代碼在我的Github上

https://github.com/hopeztm7500/AlipayServerDemo

1. RSA算法概述

如果不先說點關于資料校驗到事情,肯定對配置支付環境出現的各種密鑰十分困惑。在資料流圖中,我們發現有兩次跨應用的資料互動,前端我們吊起了支付寶,而支付寶發送同步資料給應用伺服器。特别是伺服器同步資料,原理再簡單不過,就是一個POST請求,用Shell嚴格仿照格式任何程式員都能構造出來,那麼應用伺服器如何辨識這條資料是來自支付寶伺服器而不是僞造的呢。這裡就涉及到資料簽名校驗。

簡單可以了解RSA校驗按照如下到方式工作:

(1)私鑰用來進行解密和簽名,是給自己用的。

(2)公鑰由本人公開,用于加密和驗證簽名,是給别人用的。

(3)當該使用者發送檔案時,用私鑰簽名,别人用他給的公鑰驗證簽名,可以保證該資訊是由他發送的。當該使用者接受檔案時,别人用他的公鑰加密,他用私鑰解密,可以保證該資訊隻能由他接收到。

是以在兩處資料互動的地方,我們都用到了RSA算法,在前端調用支付寶的時候,我們自己生成一對公鑰和私鑰(商戶公鑰和私鑰),公鑰我們要填寫到支付寶背景,而私鑰要自己寫在用戶端程式中。這裡我推薦把私鑰存儲在SO檔案中,防止被反編譯擷取利用。

而伺服器互動資料的時候,支付寶伺服器用自己持有的私鑰對資料做簽名,應用伺服器使用支付寶提供的公鑰對資料做校驗。是以在內建中,你會用在應用用戶端使用商戶私鑰,在支付寶背景配置商戶公鑰,在應用伺服器使用支付寶公鑰。

2. Android 用戶端內建

目錄[-]

  • 一、申請移動支付權限
  • 二、阿裡支付DEMO
  • 1、概述
  • (1)支付調用頁面及測試
  • (2)用戶端與伺服器
  • 2、配置幾個變量
  • (1)PID
  •   (2)、APPID、APP SECRET和支付寶公鑰
  • (3)、生成商戶私鑰【windows生成方法】
  • (4)、生成商戶私鑰【MAC生成方法】
  • (5)、生成使用者公鑰及網頁填充
  • 3、配置DEMO
  • 4、代碼講解
  • 第一步:構造定單資訊:
  • 第二步:對訂單字元串做RSA簽名
  • 第三步:   構造完成的請求字元串
  • 第四步:請求與結果傳回

一、申請移動支付權限

首先登入【支付寶開放平台】http://open.alipay.com/platform/home.htm,添加應用,申請移動支付權限。申請開通支付,是需要公司檔案的,個人是不允許開始支付的。

具體細節就不再詳聊了,下面就講講如何将阿裡給出的demo運作起來。

二、阿裡支付DEMO

1、概述

(1)支付調用頁面及測試

支付寶在調用時,會首先看本地是不是存在支付寶用戶端,如果有的話,就直接調用用戶端進行支付,如果沒有,則調用jar包中的H5頁面支付。

是以在測試時,需要有測試兩種情境:有支付寶用戶端和沒有支付寶用戶端的情況。

(2)、用戶端與伺服器

在demo中大家可以看到,有用戶端的demo也有服務端的demo,大家可能覺得需要服務端寫好之後,用戶端才能內建,其實并不是。整個流程是這樣的:

1,APP用戶端通過SDK發送支付請求 (用戶端處理)

2,SDK支付成功并同步傳回支付結果(用戶端處理)

3,支付寶伺服器向我們的伺服器發送支付結果字元串(服務端處理)

用戶端:從上面的流程可以看出,服務端隻是用來接出異步傳回的支付結果的。而支付與同步結果傳回都是在用戶端可以直接看得到的。是以在內建支付寶支付接口時,主要功能是在用戶端,即便服務端沒有做內建,也是可能付款成功的。

服務端:服務端隻需要添加一個功能:接口支付結果傳回

下面幾張圖顯示了整個demo的運作過程,由于沒辦法在真機上錄制gif,是以隻能用圖檔來代替了。

初始化界面:

Android用戶端和Java伺服器端內建支付寶

點選支付後,跳出确認付款界面:

Android用戶端和Java伺服器端內建支付寶

點選确認付款後,跳出輸入密碼界面:

Android用戶端和Java伺服器端內建支付寶

最後是支付成功界面:

Android用戶端和Java伺服器端內建支付寶

在看DEMO的代碼之前,我們需要先配置幾個變量:

2、配置幾個變量

這部分會對代碼中用到的幾個變量的找到方法或生成方法進行講述,部分資料引自支付寶開放平台。

(1)PID

合作者身份ID(PID)是商戶與支付寶簽約後,商戶獲得的支付寶商戶唯一識别碼。當商戶把支付寶功能接入商戶網站時會用到PID,以便讓支付寶認證商戶。

檢視PID步驟如下:

1、登入支付寶官方網站b.alipay.com

2、點選導航欄中“商家服務”

Android用戶端和Java伺服器端內建支付寶

3、點選“查詢PID、Key”

Android用戶端和Java伺服器端內建支付寶

(2)、APPID、APP SECRET和支付寶公鑰

在https://openhome.alipay.com/platform/createApp.htm頁面,建立一個應用

Android用戶端和Java伺服器端內建支付寶

完成之後:在我的應用中是可以看得到的:

Android用戶端和Java伺服器端內建支付寶

然後轉到帳戶基本資訊頁面:https://openhome.alipay.com/platform/keyManage.htm

Android用戶端和Java伺服器端內建支付寶

在開放平台密鑰欄,可以找到APPID,APP SECRET,和支付寶密鑰

這三個資料,都是在應用建立後,支付寶為我們生成好的,無法更改!

(3)、生成商戶私鑰【windows生成方法】

(有關mac的生成方法,下面會再補充)

1、下載下傳DEMO及SDK

到文檔中心,檢視移動支付對應的文檔,文檔位址:http://doc.open.alipay.com/doc2/detail?treeId=59&articleId=103563&docType=1

然後,點選(SDK&DEMO下載下傳)下載下傳代碼

Android用戶端和Java伺服器端內建支付寶

2、得到原始私鑰

在代碼中的DEMO/openssl/bin目錄下,有openssl.exe檔案

Android用戶端和Java伺服器端內建支付寶

打開openssl.exe

輸入

genrsa -out rsa_private_key.pem 1024
           

得到生成成功的結果,如下圖:

Android用戶端和Java伺服器端內建支付寶

此時,我們可以在bin檔案夾中看到一個檔案名為rsa_private_key.pem的檔案

Android用戶端和Java伺服器端內建支付寶

用記事本方式打開它,可以看到-----BEGIN RSA PRIVATE KEY-----開頭,-----END RSA PRIVATE KEY-----結尾的沒有換行的字元串,這個就是原始的私鑰。

Android用戶端和Java伺服器端內建支付寶

但這段原始私鑰代碼中是用不到的,我們需要将它轉化為PKCS8格式

3、轉換為PKCS8格式

在openssl.exe中輸入:并回車

pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt
           

得到生成功的結果,這個結果就是PKCS8格式的私鑰,如下圖:

Android用戶端和Java伺服器端內建支付寶

注意,私鑰是紅框包括的那部分,是不包含BEGIN PRIVATE KEY和END PRIVATE KEY這兩行的。

右鍵點選openssl視窗上邊邊緣,選擇編輯→标記,選中要複制的文字(如上圖),

此時繼續右鍵點選openssl視窗上邊邊緣,選擇編輯→複制,

把複制的内容粘土進一個新的記事本中,可随便命名,隻要知道這個是PKCS8格式的私鑰即可。

(4)、生成商戶私鑰【MAC生成方法】

這裡來講一下mac端如何生成使用者私鑰的,由于mac系統是自帶openssl的,是以隻需要打開終端,利用cd 指令切到任意一個想存放生成Key的檔案夾下:

比如,切到下載下傳目錄下

Android用戶端和Java伺服器端內建支付寶

然後運作下面的指令來生成私鑰原始密鑰

openssl genrsa -out rsa_private_key.pem 1024
           

然後運作下面的指令來生成轉換的PCKS8格式的指令。

openssl pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt
           

然後将生成的私鑰複制儲存起來。

從上面的指令可以看出,與windows相比,mac上需要在前面添加openssl指定運作的是openssl指令。其它指令是完全一緻的。

(5)、生成使用者公鑰及網頁填充

1、生成公鑰

同樣對于windows使用者而言,直接在openssl.exe中輸入下面的指令:

rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
           

同樣,如果是Mac的同學,輸入的指令應該是如下:

openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
           

得到生成成功的結果,如下圖:

Android用戶端和Java伺服器端內建支付寶

此時,我們可以在bin檔案夾中看到一個檔案名為rsa_public_key.pem的檔案,用記事本方式打開它,可以看到-----BEGIN PUBLIC KEY-----開頭,

-----END PUBLIC KEY-----結尾的沒有換行的字元串,這個就是公鑰。

Android用戶端和Java伺服器端內建支付寶
Android用戶端和Java伺服器端內建支付寶

在生成網頁以後,複制----BEGIN PUBLIC KEY-----和-----END PUBLIC KEY-----之間的部分,即那段純代碼,不要把----BEGIN PUBLIC KEY-----和-----END PUBLIC KEY-----給複制進去了。中間的這部分就是公鑰。

2、網頁填充

然後到https://openhome.alipay.com/platform/keyManage.htm?keyType=partner(需要登入)中,左側找到合作夥伴密鑰欄,再到右側的RSA加密中,将公鑰粘貼進去。由于,我們已經粘貼進去了,是以這裡顯示檢視開發者公鑰,在沒填之前寫的是“添加開發者公鑰”

Android用戶端和Java伺服器端內建支付寶

到這裡,所有的準備工作都已經結束了。下面就是配置DEMO的過程了

3、配置DEMO

在剛才下載下傳的sdk&demo的源碼中,打開DEMO/用戶端demo/支付寶Android 15.0.1/alipay_demo工程

路徑如下:

Android用戶端和Java伺服器端內建支付寶

在PayDemoActivity中配置幾個變量:

//PID
public static final String PARTNER = "";
           

在這裡填上我們上面找到的PID;

// 商戶收款賬号
public static final String SELLER = "[email protected]";
           

然後在SELLER上寫上我們支付寶的登入帳戶,即那個你申請移動支付的支付寶賬号

// 支付寶公鑰
public static final String RSA_PUBLIC ="";
           

然後在RSA_PUBLIC這裡填上支付寶公鑰

// 商戶私鑰,pkcs8格式
public static final String RSA_PRIVATE = "";
           

最後是填上RSA_PRIVATE對應的商戶私鑰,注意是PKCS8格式的。

私鑰這部分,注意是----BEGIN PUBLIC KEY-----和-----END PUBLIC KEY-----之間的部分,即那段純代碼,不要把----BEGIN PUBLIC KEY-----和-----END PUBLIC KEY-----給複制進去了。中間的這部分就是公鑰。

Android用戶端和Java伺服器端內建支付寶

現在運作demo就直接可以支付了。

本文中對應的DEMO在文章底部給出。

4、代碼講解

通過上面的配置,demo應該就直接可以運作了,但這裡所涉及的代碼,我們再仔細看看

主要的支付與結果傳回就是pay()這個函數,這裡完成了支付所需要的所有功能。代碼如下:

public void pay(View v) {
    …………
    // 訂單資訊
    String orderInfo = getOrderInfo("測試的商品", "該測試商品的較長的描述", "0.01");

    // 對訂單做RSA 簽名
    String sign = sign(orderInfo);
    try {
        // 僅需對sign 做URL編碼
        sign = URLEncoder.encode(sign, "UTF-8");
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }

    // 完整的符合支付寶參數規範的訂單資訊
    final String payInfo = orderInfo + "&sign=\"" + sign + "\"&"
            + getSignType();

    Runnable payRunnable = new Runnable() {

        @Override
        public void run() {
            // 構造PayTask 對象
            PayTask alipay = new PayTask(PayDemoActivity.this);
            // 調用支付接口,擷取支付結果
            String result = alipay.pay(payInfo);

            Message msg = new Message();
            msg.what = SDK_PAY_FLAG;
            msg.obj = result;
            mHandler.sendMessage(msg);
        }
    };

    // 必須異步調用
    Thread payThread = new Thread(payRunnable);
    payThread.start();
}
           

這裡總是分了四步來完成支付與結果接收。

第一步:構造定單資訊:

String orderInfo = getOrderInfo("測試的商品", "該測試商品的較長的描述", "0.01");
           

主要是這句,即在getOrderInfo()函數中完成定單資訊的構造:(這裡對getOrderInfo函數做的精減,更多字段及意義參考源碼)

有關paymethod的方法使用,參考:https://cshall.alipay.com/support/help_detail.htm?help_id=476935

各個字段的意義及取值參考:http://doc.open.alipay.com/doc2/detail?treeId=59&articleId=103663&docType=1

public String getOrderInfo(String subject, String body, String price) {

    // 簽約合作者身份ID
    String orderInfo = "partner=" + "\"" + PARTNER + "\"";

    // 簽約賣家支付寶賬号
    orderInfo += "&seller_id=" + "\"" + SELLER + "\"";

    // 商戶網站唯一訂單号
    orderInfo += "&out_trade_no=" + "\"" + getOutTradeNo() + "\"";

    // 商品名稱
    orderInfo += "&subject=" + "\"" + subject + "\"";

    // 商品詳情
    orderInfo += "&body=" + "\"" + body + "\"";

    // 商品金額
    orderInfo += "&total_fee=" + "\"" + price + "\"";

    // 伺服器異步通知頁面路徑
    orderInfo += "&notify_url=" + "\"" + "http://notify.msp.hk/notify.htm"
            + "\"";

    …………
    return orderInfo;
}
           

這裡就是通過我們的提供的商家ID,産品資訊,價格等資訊來構造定單及回調頁面,這裡需要非常注意的一個地方:

// 伺服器異步通知頁面路徑
orderInfo += "&noify_url=" + "\"" + "http://notify.msp.hk/notify.htm"
        + "\"";
           

伺服器異步通知頁面路徑,首先我們用支付寶支付之後,支付寶會傳回給我們兩個通知,一個是同步的,就是我們點選支付後支付寶直接回報給我們用戶端的資訊,我們可以直接拿到,根據回報的結果可以初步判定該次交易是否成功,第二個就是伺服器異步的通知,這個異步的通知是支付寶的伺服器端發給我們伺服器端的資訊,我們在用戶端是直接擷取不了的,那支付寶的伺服器怎麼知道我們伺服器的路徑呢,那就是這參數的作用了,我們給支付寶伺服器一個路徑,它就會在訂單狀态改變的時候給我們伺服器端一個回報,告訴伺服器這次交易的狀态,如果伺服器結果判定該次交易成功了,就必須返給支付寶伺服器一個success,要不伺服器會一直給我們異步通知,因為它不知道該次交易是否完成了(一般情況下25小時内8次通知,頻率一般是2m 10m 10m 1h 2h 6h 15h),我們一般會在收到異步通知時,對訂單的狀态進行更新。

其它的就不講了,通過看源碼都能看得懂,比如構造訂單号啥的。

第二步:對訂單字元串做RSA簽名

為什麼要簽名呢?當然是防止傳輸出錯了,這可是跟錢相關的,如果orderInfo傳輸過程中出錯了,那怎麼樣來校驗它是不是出錯了呢,隻有通過簽名算法來了。是以這裡就需要對訂單字元串做簽名。

具體簽名算法就不講了,直接應用到項目中就行,不需要了解,如果想看看怎麼實作的,裡面有對應的源碼,可以去研究一下。

// 對訂單做RSA 簽名
String sign = sign(orderInfo);
try {
    // 僅需對sign 做URL編碼
    sign = URLEncoder.encode(sign, "UTF-8");
} catch (UnsupportedEncodingException e) {
    e.printStackTrace();
}
           

第三步:構造完成的請求字元串

在訂單字元串和簽名做完以後,就可以用他們來構造完整的請求字元串了:

// 完整的符合支付寶參數規範的訂單資訊
final String payInfo = orderInfo + "&sign=\"" + sign + "\"&"
        + getSignType();
           

第四步:請求與結果傳回

最後是發送請求,代碼如下:

Runnable payRunnable = new Runnable() {

    @Override
    public void run() {
        // 構造PayTask 對象
        PayTask alipay = new PayTask(PayDemoActivity.this);
        // 調用支付接口,擷取支付結果
        String result = alipay.pay(payInfo);

        Message msg = new Message();
        msg.what = SDK_PAY_FLAG;
        msg.obj = result;
        mHandler.sendMessage(msg);
    }
};

// 必須異步調用
Thread payThread = new Thread(payRunnable);
payThread.start();
           

最關鍵的部分在這裡:

PayTask alipay = new PayTask(PayDemoActivity.this);
// 調用支付接口,擷取支付結果
String result = alipay.pay(payInfo);

Message msg = new Message();
msg.what = SDK_PAY_FLAG;
msg.obj = result;
mHandler.sendMessage(msg);
           

在String result = alipay.pay(payInfo);中,就直接獲得了支付結果;

然後通過handler将結果發送出去。

3. 伺服器內建

內建了用戶端,工作才剛剛完成一半,支付寶用戶端傳回了支付結果後,應用伺服器的資料庫中,訂單的狀态并沒有改變,如果依賴用戶端通知伺服器改變訂單狀态,那是十分危險的,就好像剛剛說過的一樣,任何程式員經過一定的努力,都可能發送資料給你的應用伺服器,改變你訂單的狀态,這樣,即使沒有支付,訂單的狀态也會變成已支付,是以,訂單狀态的改變,一定要依賴伺服器的資料互動。

那麼目前端完成支付以後,資料如何互動呢?notify_url !

還記得前端構造的訂單資料中的notify_url麼,支付寶伺服器就是利用這個配置,将伺服器資料POST到這個位址上的,是以在進行伺服器內建之前,你要保證你的伺服器已經能夠接收到請求。

一份完整的代碼可以參考:https://github.com/hopeztm7500/AlipayServerDemo

1. 伺服器同步的訂單資料

支付寶伺服器發送給我們的資料,可以用如下的Bean來描述。

package com.wenxi.alipay.bean;

import java.io.Serializable;
import java.util.Date;

import com.alibaba.fastjson.annotation.JSONField;

public class AlipayNotification implements Serializable{

    /**
	 * 
	 */
    private static final long serialVersionUID = -8638199167144867399L;

    private Integer alipayNoticeId;

    private String notifyId;

    private String notifyType;

    @JSONField(format="yyyy-MM-dd HH:mm:ss")
    private Date notifyTime;

    private String signType;

    private String sign;

    private String outTradeNo;

    private String subject;

    private String paymentType;

    private String tradeNo;

    private String tradeStatus;

    private String sellerId;

    private String sellerEmail;

    private String buyerId;

    private String buyerEmail;

    private Double totalFee;

    private Integer quantity;

    private Double price;

    private String body;

    @JSONField(format="yyyy-MM-dd HH:mm:ss")
    private Date gmtCreate;

    @JSONField(format="yyyy-MM-dd HH:mm:ss")
    private Date gmtPayment;

    private String isTotalFeeAdjust;

    private String userCoupon;

    private String discount;

    private String refundStatus;

    @JSONField(format="yyyy-MM-dd HH:mm:ss")
    private Date gmtRefund;

    private Boolean verifyResult;

    public String getNotifyId() {
        return notifyId;
    }

    public void setNotifyId(String notifyId) {
        this.notifyId = notifyId == null ? null : notifyId.trim();
    }

    public String getNotifyType() {
        return notifyType;
    }

    public void setNotifyType(String notifyType) {
        this.notifyType = notifyType == null ? null : notifyType.trim();
    }

    public Date getNotifyTime() {
        return notifyTime;
    }

    public void setNotifyTime(Date notifyTime) {
        this.notifyTime = notifyTime;
    }

    public String getSignType() {
        return signType;
    }

    public void setSignType(String signType) {
        this.signType = signType == null ? null : signType.trim();
    }

    public String getSign() {
        return sign;
    }

    public void setSign(String sign) {
        this.sign = sign == null ? null : sign.trim();
    }

    public String getOutTradeNo() {
        return outTradeNo;
    }

    public void setOutTradeNo(String outTradeNo) {
        this.outTradeNo = outTradeNo == null ? null : outTradeNo.trim();
    }

    public String getSubject() {
        return subject;
    }

    public void setSubject(String subject) {
        this.subject = subject == null ? null : subject.trim();
    }

    public String getPaymentType() {
        return paymentType;
    }

    public void setPaymentType(String paymentType) {
        this.paymentType = paymentType == null ? null : paymentType.trim();
    }

    public String getTradeNo() {
        return tradeNo;
    }

    public void setTradeNo(String tradeNo) {
        this.tradeNo = tradeNo == null ? null : tradeNo.trim();
    }

    public String getTradeStatus() {
        return tradeStatus;
    }

    public void setTradeStatus(String tradeStatus) {
        this.tradeStatus = tradeStatus == null ? null : tradeStatus.trim();
    }

    public String getSellerId() {
        return sellerId;
    }

    public void setSellerId(String sellerId) {
        this.sellerId = sellerId == null ? null : sellerId.trim();
    }

    public String getSellerEmail() {
        return sellerEmail;
    }

    public void setSellerEmail(String sellerEmail) {
        this.sellerEmail = sellerEmail == null ? null : sellerEmail.trim();
    }

    public String getBuyerId() {
        return buyerId;
    }

    public void setBuyerId(String buyerId) {
        this.buyerId = buyerId == null ? null : buyerId.trim();
    }

    public String getBuyerEmail() {
        return buyerEmail;
    }

    public void setBuyerEmail(String buyerEmail) {
        this.buyerEmail = buyerEmail == null ? null : buyerEmail.trim();
    }

    public Double getTotalFee() {
        return totalFee;
    }

    public void setTotalFee(Double totalFee) {
        this.totalFee = totalFee;
    }

    public Integer getQuantity() {
        return quantity;
    }

    public void setQuantity(Integer quantity) {
        this.quantity = quantity;
    }

    public Double getPrice() {
        return price;
    }

    public void setPrice(Double price) {
        this.price = price;
    }

    public String getBody() {
        return body;
    }

    public void setBody(String body) {
        this.body = body == null ? null : body.trim();
    }

    public Date getGmtCreate() {
        return gmtCreate;
    }

    public void setGmtCreate(Date gmtCreate) {
        this.gmtCreate = gmtCreate;
    }

    public Date getGmtPayment() {
        return gmtPayment;
    }

    public void setGmtPayment(Date gmtPayment) {
        this.gmtPayment = gmtPayment;
    }

    public String getIsTotalFeeAdjust() {
        return isTotalFeeAdjust;
    }

    public void setIsTotalFeeAdjust(String isTotalFeeAdjust) {
        this.isTotalFeeAdjust = isTotalFeeAdjust == null ? null : isTotalFeeAdjust.trim();
    }

    public String getUserCoupon() {
        return userCoupon;
    }

    public void setUserCoupon(String userCoupon) {
        this.userCoupon = userCoupon == null ? null : userCoupon.trim();
    }

    public String getDiscount() {
        return discount;
    }

    public void setDiscount(String discount) {
        this.discount = discount == null ? null : discount.trim();
    }

    public String getRefundStatus() {
        return refundStatus;
    }

    public void setRefundStatus(String refundStatus) {
        this.refundStatus = refundStatus == null ? null : refundStatus.trim();
    }

    public Date getGmtRefund() {
        return gmtRefund;
    }

    public void setGmtRefund(Date gmtRefund) {
        this.gmtRefund = gmtRefund;
    }

    public Integer getAlipayNoticeId() {

		return alipayNoticeId;
    }

    public void setAlipayNoticeId(Integer alipayNoticeId) {

	this.alipayNoticeId = alipayNoticeId;
    }

	public Boolean getVerifyResult() {

		return verifyResult;
	}

	public void setVerifyResult(Boolean verifyResult) {

		this.verifyResult = verifyResult;
	}

}
           

當然擷取這些資料需要一些額外的工作,首先它們存在Request對象中,其次它們是underscore case,是以Sample代碼中,先把這些資料用AsynAlipayNotifyController接收到,并且轉化成我們能夠處理的Bean格式。

其中,outTradeNo可能我們最關心的業務資料,是應用伺服器中的訂單ID,狀态的改變幾乎全憑它。

2. 資料校驗

資料校驗的代碼,基本上就是支付寶Java伺服器Demo中的代碼,需要配置的隻有PID,代碼中已經提供了支付寶公鑰,這裡不需要修改。

資料校驗的核心代碼在這裡:

public static boolean verify(Map<String, String> params) {
        // 判斷responsetTxt是否為true,isSign是否為true
        // responsetTxt的結果不是true,與伺服器設定問題、合作身份者ID、notify_id一分鐘失效有關
	// isSign不是true,與安全校驗碼、請求時的參數格式(如:帶自定義參數等)、編碼格式有關
	String responseTxt = "false";
	if (params.get("notify_id") != null) {
		String notify_id = params.get("notify_id");
		responseTxt = verifyResponse(notify_id);
	}
	String sign = "";
	if (params.get("sign") != null) {
	        sign = params.get("sign");
	}
	boolean isSign = getSignVeryfy(params, sign);
	if (isSign && responseTxt.equals("true")) {
		return true;
	} else {
		return false;
	}
}      

首先校驗通知ID,這個ID的是有有效期的,是以如果用測試資料POST到伺服器,可能還要注意ID是不是過期,然後就是簽名校驗,簽名校驗使用了支付寶公鑰。然後根據校驗結果,就可以實作相應的業務邏輯了。

寫到這裡, 第三方支付-支付寶的應用內建,基本就理清了。不過,總結起來,技術都不是難點,如何讓使用者塞錢到你的支付寶,才是我們的最終目标~ 祝每個內建後的小夥伴,都有現金流進來!

傳送門 http://sunrising.me/?p=74

繼續閱讀