支付宝使用总结

引言

写这篇文章主要是总结一下支付宝的使用,因为最近这个项目要添加一个支付宝功能,所有的资料都是通过查看蚂蚁金服开发者平台和其他开发者写的一些资料加上自己的汇总得来的

但是呢,不幸的是这个项目不能采用第三方支付,因为购买的是虚拟产品,更新审核很大可能不会给过,有人说可以通过控制后台开关进行控制前台支付功能的隐藏,过了审核再打开开关,但是风险还是比较大的,被发现了那就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算法,有时间我去研究研究,这个不是重点
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容