支付寶使用總結

引言

寫這篇文章主要是總結一下支付寶的使用,因為最近這個項目要添加一個支付寶功能,所有的資料都是通過查看螞蟻金服開發者平臺和其他開發者寫的一些資料加上自己的匯總得來的

但是呢,不幸的是這個項目不能采用第三方支付,因為購買的是虛擬產品,更新審核很大可能不會給過,有人說可以通過控制后臺開關進行控制前臺支付功能的隱藏,過了審核再打開開關,但是風險還是比較大的,被發現了那就GG

這個項目采用的是內購,支付寶呢我自己研究下,這里就總結一下以后有機會的話我再嘗試了,大差不差

好了,廢話不多說了,開始進入正題

一、準備工作

需要的東西:

  • 商戶唯一的PID(partner的簡稱):是商戶與支付寶簽約后,商戶獲得的支付寶商戶唯一標識碼.當商戶把支付寶功能介入商戶網站時會PID,以便讓支付寶認證商戶.
  • 賬號ID(sellerID):用戶購買的支付賬號,訂單支付金額將打入該賬戶,一個partner可以對應多個seller_id。
  • 下載相應的公鑰私鑰(加密簽名使用,RSA加密)
    其中前兩個一般都是公司負責的,但是非要你弄的的話也沒事:

1.首先你需要有一個支付寶賬號(最好公司的)登錄螞蟻金服實名認證這個支付寶賬號,需要視同企業資料,成為企業支付寶賬號.
2.登錄支付寶官方網站支付寶商家中心,與支付寶進行簽約.
3.然后進入開發者平臺,創建應用,官方文檔
4.按照官方文檔走,生成公鑰和私鑰以及上述的幾個東西

注:其中RSA密鑰私鑰有兩個,一個是pkcs8密鑰和普通密鑰,這里我們使用pkcs8密鑰,另外公鑰是用來在開發平臺的應用配置中添加公鑰,同時會生成一個支付寶公鑰.

二、集成支付寶SDK

1.支付寶SDK下載地址為:這里
PS:另外還可以下載對應服務端SDK,各種不同的開發語言都有集成示例.

2.來看看我們的SDK里需要的東西:

支付寶SDK內的玩意.png

我們需要的東西如下:

需要的東西.png

將這幾個文件放到一個文件夾中并命名為AliPaySDK拖入項目中.

3.下面就是環境的配置了,這個東西網上找找,相信你可以配置成功的,就不多說了,給你兩個地址好了:
官方文檔環境配置
民間高手配置
出了什么問題,結合這兩個就可以了.

三、支付寶支付流程

這里是借鑒別人的支付流程了,你看

支付流程.png

支付過程:
1.用戶向商城網站發起確認訂單的請求
2.商城網站接收到請求保存訂單數據到數據庫或其他存儲介質
3.返回訂單確認頁面,頁面上應該顯示訂單金額等信息
4.用戶確認支付,發起支付請求.注:支付請求時發送到支付網關(比如支付寶,網銀在線等)而不是發送到商城網站
5.顯示支付頁面
6.用戶填寫認證信息(賬號密碼等)提交
7.這里有兩個步驟,一個是扣款成功后頁面跳轉到支付結果頁面(展示給用戶),另一個是支付通知,這兩步沒有先后順序可能同時執行,商城網站接收到支付通知后根據驗證規則驗證信息的有效性,并作出相應的更改操作(例:有效則更改訂單為已付款裝填,無效則記錄非法請求信息).

那么我們注意的就是第4步和第7步

四、客戶端代碼的實現

入口類

支付寶客戶端打開

- (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)url
  sourceApplication:(NSString *)sourceApplication
         annotation:(id)annotation {
    
    if ([url.host isEqualToString:@"safepay"]) {
        //跳轉支付寶錢包進行支付,處理支付結果
        [[AlipaySDK defaultService] processOrderWithPaymentResult:url standbyCallback:^(NSDictionary *resultDic) {
            NSLog(@"result = %@",resultDic);
        }];
    }
    return YES;
}

// NOTE: 9.0以后使用新API接口
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options
{
    if ([url.host isEqualToString:@"safepay"]) {
        //跳轉支付寶錢包進行支付,處理支付結果
        [[AlipaySDK defaultService] processOrderWithPaymentResult:url standbyCallback:^(NSDictionary *resultDic) {
            NSLog(@"result = %@",resultDic);
        }];
    }
    return YES;
}
生成訂單信息
 /*
     *商戶的唯一的parnter和seller。
     *簽約后,支付寶會為每個商戶分配一個唯一的 parnter 和 seller。
     */
  /*============================================================================*/
    /*=======================需要填寫商戶app申請的===================================*/
    /*============================================================================*/
    NSString *partner = PID;
    NSString *seller = @"";
    NSString *privateKey = kPrivateKey;
    //支付寶公鑰(目前所有支付寶公鑰都是這個)
    NSString* key = @"";
    //這個方法應該是初始化公鑰并保存到本地吧
    id<DataVerifier> verifier = CreateRSADataVerifier(key);
    /*============================================================================*/
    /*============================================================================*/
    /*============================================================================*/
    
    //partner和seller獲取失敗,提示
    if ([partner length] == 0 ||
        [seller length] == 0 ||
        [privateKey length] == 0)
    {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示"
                                                        message:@"缺少partner或者seller或者私鑰。"
                                                       delegate:self
                                              cancelButtonTitle:@"確定"
                                              otherButtonTitles:nil];
        [alert show];
        return;
    }
    
    /*
     *生成訂單信息及簽名
     */
    //將商品信息賦予AlixPayOrder的成員變量
    Order *order = [[Order alloc] init];
    order.partner = partner;
    order.sellerID = seller;
    order.outTradeNO = @"10087"; //訂單ID(由商家自行制定)
    order.subject = @"火車票"; //商品標題
    order.body = @"這是從北京到蕪湖的火車票"; //商品描述
    order.totalFee = @"0.01"; //商品價格
    order.notifyURL =  @"http://www.baidu.com"; //回調URL
    
    order.service = @"mobile.securitypay.pay";
    order.paymentType = @"1";
    order.inputCharset = @"utf-8";
    order.itBPay = @"30m";
    order.showURL = @"m.alipay.com";
    ```

#####應用注冊scheme,在AlixPayDemo-Info.plist定義URL types
注意這一步
NSString *appScheme = @"alisdkdemo";
#####將商品拼接成字符串
    
//將商品信息拼接成字符串
NSString *orderSpec = [order description];
NSLog(@"orderSpec = %@",orderSpec);

//獲取私鑰并將商戶信息簽名,外部商戶可以根據情況存放私鑰和簽名,只需要遵循RSA簽名規范,并將簽名字符串base64編碼和UrlEncode
id<DataSigner> signer = CreateRSADataSigner(privateKey);
NSString *signedString = [signer signString:orderSpec];

//將簽名成功字符串格式化為訂單字符串,請嚴格按照該格式
NSString *orderString = nil;
if (signedString != nil) {
    orderString = [NSString stringWithFormat:@"%@&sign=\"%@\"&sign_type=\"%@\"",
                   orderSpec, signedString, @"RSA"];
    
    [[AlipaySDK defaultService] payOrder:orderString fromScheme:appScheme callback:^(NSDictionary *resultDic) {
        NSLog(@"返回結果resultDic = %@",resultDic);
#####支付結果頁面驗證
        if (resultDic)
        {
            /*
             9000 訂單支付成功
             8000 正在處理中
             4000 訂單支付失敗
             6001 用戶中途取消
             6002 網絡連接出錯
             */
            if ([resultDic[@"resultStatus"]integerValue] == 9000)
                //網上很多教程到這里就結束了,因為他們沒有驗證返回訂單簽名
            {
                //驗簽
                //去掉返回字典中result值里面的“\\”
                NSString *result = [resultDic[@"result"] stringByReplacingOccurrencesOfString:@"\\\\" withString:@""];
                //分割字符串獲取訂單信息和簽名

// [self.result componentsSeparatedByString:@"&sign_type="RSA"&sign="][0];
NSArray *array = [result componentsSeparatedByString:@"&sign_type="RSA"&sign="];
//返回的訂單信息
NSString *orderString = array[0];
//返回的訂單簽名
NSString *signedString = [array[1] substringToIndex:[array[1]length]-1];
//驗證返回信息與簽名
if ([verifier verifyString:orderString withSign:signedString])
{
//驗證簽名成功,交易結果無篡改
NSLog(@"------------支付成功---------------");
}
else
{
//驗簽錯誤
}

                  }
                  
                  }
                  else
                  {
                      //交易失敗
                  }
    }];
}
這里實現由上述步驟中的第4步以及第7步中的支付結果頁面(這個是展示給用戶的)
**注意:關于第4步的密鑰加簽和第7步的驗證最好放到服務器端做,以保證安全**


這個是支付請求接口的callback返回的resultDic,擺出來方便研究

po resultDic
{
memo = “";
result = "partner="2088121307144063"&seller_id="service@9elephas.com"&out_trade_no="10086"&subject="\U706b\U8f66\U7968"&body="\U8fd9\U662f\U4ece\U5317\U4eac\U5230\U829c\U6e56\U7684\U706b\U8f66\U7968"&total_fee="0.01"&notify_url="http://www.baidu.com"&service="mobile.securitypay.pay"&payment_type="1"&_input_charset="utf-8"&it_b_pay="30m"&show_url="m.alipay.com"&success="true"&sign_type="RSA"&sign="Qtqv2CjSf2WXdg37cLKiN0b+nCdJsAk+deB95c+qykyz1F9Zc2sglc+6/tReTNoIFbX4D3jS31KMYEuDHuwx8ibm8fyDYpAPxHsD1x6+MaoBpXAe2UtsF6v7xFGzhmieJ8hG4WYgUhxp6LrxtSBHuxreI+pjMSeaMA0qZEvJj4g="";
resultStatus = 9000;
}


##五、接口問題
這里講述的東西還是上面所說的第4步和第7步

// 支付寶SDK支付請求接口

  • (void)payOrder:(NSString *)orderStr
    fromScheme:(NSString *)schemeStr
    callback:(CompletionBlock)completionBlock;
這個方法是用來發送支付請求,也就是第4步中的確認支付(發起支付請求)
另外還有一個方法
  • (void)processOrderWithPaymentResult:(NSURL *)resultUrl
    standbyCallback:(CompletionBlock)completionBlock;
  這兩方法的區別之處主要是區分你的手機有沒有下載支付寶客戶端,如果沒有下載支付寶客戶端,在你支付完成后,支付寶服務器會通過callback返回信息,反之會在standbyCallback返回信息.

在上面的代碼中可以看到,我們在返回信息的block中做了驗簽操作,當然這個最好是放在服務器端進行.我們除了在這里做驗簽操作之外,我們還需要做些可視化的操作,比如說通知用戶這筆交易的成功與否,需要給用戶展示什么樣的東西等等...(注意,我們這里還沒有對服務器數據庫等做更新訂單的操作)

同時當我們閱讀[開放平臺文檔](https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.A8CMxI&treeId=193&articleId=105302&docType=1)的時候會發現他有兩種通知,一種是客戶端同步返回,一種是支付結果異步通知,其主要區別如下:
其官方文檔是這樣說的:
>支付寶SDK對商戶的請求支付(payOrder方法)數據處理完成后,會將結果同步反饋給商戶app端.
>**同步返回的數據,商戶可以按照下文描述的方式在服務端
驗證,驗證通過后,可以認為本次用戶付款成功。有些時候會出現商戶app在支付寶付款階段被關閉導致無法正確收到同步結果,此時支付結果可以完全依賴服務端的異步通知。**
>**由于同步通知和異步通知都可以作為支付完成的憑證,且異步通知支付寶一定會確保發送給商戶服務端。為了簡化集成流程,商戶可以將同步結果僅僅作為一個支付結束的通知(忽略執行校驗),實際支付是否成功,完全依賴服務端異步通知。
**

這里我來解釋下,意思就是說當你支付完成后,支付寶頁面會自動跳轉到之前的界面,但是會有這樣的情況發生,就是當你界面還沒跳轉時,用戶自己將該頁面關閉,那就無法正確的收到同步結果了,因此這里還必須得采用服務器端異步通知來保持訂單的同步.

至于同步操作就不多說了,在這里我們主要做兩件事,一件就是驗簽,保證訂單的可靠性,另外就是一些可視化操作,讓用戶知道交易成功與否.
這里是官方文檔對于客戶端同步返回的介紹:
>**第四步:** 驗證簽名是否合法:
使用各自語言對應的SHA1WithRSA簽名驗證函數,傳入簽名的原始字符串、支付寶公鑰、簽名類型RSA、簽名字符進行合法性驗證。
**第五步: 在第四步簽名驗證通過后,必須嚴格按照如下的描述校驗通知參數的合法性:**
**1、商戶需要驗證該通知數據中的out_trade_no是否為商戶系統中創建的訂單號;2、判斷total_amount是否確實為該訂單的實際金額(即商戶訂單創建時的金額);3、校驗通知中的seller_id(或者seller_email) 是否為out_trade_no這筆單據對應的操作方(有的時候,一個商戶可能有多個seller_id/seller_email);4、驗證app_id是否為該商戶本身。上述1、2、3、4有任何一個驗證不通過,則表明同步校驗結果是無效的,只有全部驗證通過后,才可以認定買家付款成功。**

##六、支付結果異步通知
>看官方文檔中的介紹:**對于App支付產生的交易,支付寶會根據原始支付API中傳入的異步通知地址notify_url,通過POST請求的形式將支付結果作為參數通知到商戶系統。**

這里的異步通知地址就是之前構建訂單信息中的回調地址notify_url

再看,服務器異步通知頁面特性:
>必須保證服務器異步通知頁面(notify_url)上無任何字符,如空格、HTML標簽、開發系統自帶拋出的異常提示信息等;

>支付寶是用POST方式發送通知信息,因此該頁面中獲取參數的方式,如:request.Form(“out_trade_no”)、$_POST[‘out_trade_no’];

>支付寶主動發起通知,該方式才會被啟用;

>只有在支付寶的交易管理中存在該筆交易,且發生了交易狀態的改變,支付寶才會通過該方式發起服務器通知(即時到賬交易狀態為“等待買家付款”的狀態默認是不會發送通知的);

>服務器間的交互,不像頁面跳轉同步通知可以在頁面上顯示出來,這種交互方式是不可見的;

>第一次交易狀態改變(即時到賬中此時交易狀態是交易完成)時,不僅會返回同步處理結果,而且服務器異步通知頁面也會收到支付寶發來的處理結果通知;

>程序執行完后必須打印輸出“success”(不包含引號)。如果商戶反饋給支付寶的字符不是success這7個字符,支付寶服務器會不斷重發通知,直到超過24小時22分鐘。一般情況下,25小時以內完成8次通知(通知的間隔頻率一般是:4m,10m,10m,1h,2h,6h,15h);

>程序執行完成后,該頁面不能執行頁面跳轉。如果執行頁面跳轉,支付寶會收不到success字符,會被支付寶服務器判定為該頁面程序運行出現異常,而重發處理結果通知;

>cookies、session等在此頁面會失效,即無法獲取這些數據;

>該方式的調試與運行必須在服務器上,即互聯網上能訪問;
該方式的作用主要防止訂單丟失,即頁面跳轉同步通知沒有處理訂單更新,它則去處理;

>當商戶收到服務器異步通知并打印出success時,服務器異步通知參數notify_id才會失效。也就是說在支付寶發送同一條異步通知時(包含商戶并未成功打印出success導致支付寶重發數次通知),服務器異步通知參數notify_id是不變的。

這里需要注意的是:
他是當訂單狀態發生變化時,支付寶服務器才會通知這個頁面,然后在這個頁面做驗簽,更新服務器數據庫等操作.
另外驗簽通過后需要返回success,不然會重復通知.


###總結
這里總結一下,支付寶這一塊最重要的應該就是上面說的確認支付和返回結果頁面以及支付通知信息這一塊,搞定了就不難了,當然還有RSA算法,有時間我去研究研究,這個不是重點
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容