http://blog.csdn.net/dynastyting/article/details/51304293
http://blog.csdn.net/dynastyting/article/details/51312271
ios中
[[AlipaySDKdefaultService]payOrder:orderStringfromScheme:appSchemecallback:^(NSDictionary *resultDic) {
//callback处理支付结果为同步通知
NSLog(@"reslut = %@",resultDic);
}];
resultDic为形如以下字典
memo = "";
result = "partner=\"2088221664440135\"&seller_id=\"qmmodify@126.com\"&out_trade_no=\"Q34FAVRNJFTF12Z\"&subject=\"x5\U6fc0\U5149,x5\U706b\U82b1\"&body=\"\U6b64\U6b21X5\U7684\U6539\U6b3e\U66f4\U91cd\U8981\U7684\U4e00\U70b9\U5c31\U662f\U589e\U5f3a\U5e74\U8f7b\U6c14\U606f\Uff0c\U5c3e\U90e8\U867d\U7136\U4fdd\U7559\U4e86\U4e0a\U4e00\U4ee3\U7a33\U91cd\U7684\U6c14\U606f\Uff0c\U4f46\U6574\U4f53\U7684\U7ec6\U8282\U6539\U53d8\U8ba9\U5176\U770b\U8d77\U6765\U66f4\U65f6\U5c1a\Uff0c\U66f4\U52a0\U8fd0\U52a8\Uff0c\U66f4\U5bb9\U6613\U88ab\U5e74\U8f7b\U7fa4\U4f53\U63a5\U53d7\U3002\U5176\U4e2d\U5c3e\U706f\U4f9d\U7136\U662f\U706f\U5e26\U5f0f\U8bbe\U8ba1\Uff0c\U5176\U4e2d\U8be0\U91ca\U7684\U610f\U4e49\U662f\U70e7\U7ea2\U7684\U6392\U6c14\U7ba1\Uff0c\U4ee3\U8868\U5b9d\U9a6c\U8fd0\U52a8\U7684\U7cbe\U9ad3,i\U4fc4\U56fd\U548c\U70ed\U72d7\U7ea2\U65e5\U5149\U5982\U679chi\U97e9\U56fd\U641e\U540e\U641e\U5bc4\U6b27\U4e8634\U5c3143\U6b27\U51a0\U4ef6 3\U5339\U914d \U5bf94 \U5c31\U80a9\U8d1f 34i\U7ed9\U5bb6 \Ufe3f(\Uffe3\Ufe36\Uffe3)\Ufe3f\U54e6(\U2299o\U2299)\U54e6\U4e2a\U8fea\U8fea\U4f1a43 \U653e i34jfi43j\U770b\U4e86\U70ed\U54e6\Ufe3f(\Uffe3\Ufe36\Uffe3)\Ufe3f 34\U9ad8\U7c92\U770b\U540e\U611fi4 3\U5de5i\U4e2a43\U54e6\U7684 \U5bf94\U5076\U8bb0\U5f9743\U54e6\Ufe3f(\Uffe3\Ufe36\Uffe3)\Ufe3f\U54e64\U54e6\U641e\U5594<(\Uffe3\Ufe36\Uffe3)\U2197[GO!] <(\Uffe3\Ufe36\Uffe3)\U2197[GO!]\U591f \U768434 \U56624 \U76ae\U80a43 \Ufe3f(\Uffe3\Ufe36\Uffe3)\Ufe3f\U5e73\U641e\U641e\U5e73\U5149\U76d8\U72d7\U5c41 \U62cd\U6539 \U8bc4\U4f30i3\U8bc4\U4f30IP\U7ed9 3\U7ed9 \U516c\U5e73 3\U7ed93 \U3002;\U6b64\U6b21X5\U7684\U6539\U6b3e\U66f4\U91cd\U8981\U7684\U4e00\U70b9\U5c31\U662f\U589e\U5f3a\U5e74\U8f7b\U6c14\U606f\Uff0c\U5c3e\U90e8\U867d\U7136\U4fdd\U7559\U4e86\U4e0a\U4e00\U4ee3\U7a33\U91cd\U7684\U6c14\U606f\Uff0c\U4f46\U6574\U4f53\U7684\U7ec6\U8282\U6539\U53d8\U8ba9\U5176\U770b\U8d77\U6765\U66f4\U65f6\U5c1a\Uff0c\U66f4\U52a0\U8fd0\U52a8\Uff0c\U66f4\U5bb9\U6613\U88ab\U5e74\U8f7b\U7fa4\U4f53\U63a5\U53d7\U3002\U5176\U4e2d\U5c3e\U706f\U4f9d\U7136\U662f\U706f\U5e26\U5f0f\U8bbe\U8ba1\Uff0c\U5176\U4e2d\U8be0\U91ca\U7684\U610f\U4e49\U662f\U70e7\U7ea2\U7684\U6392\U6c14\U7ba1\Uff0c\U4ee3\U8868\U5b9d\U9a6c\U8fd0\U52a8\U7684\U7cbe\U9ad3\U3002\"&total_fee=\"0.01\"¬ify_url=\"http://192.168.1.116:8080/refitcar/notify_url.jsp\"&service=\"mobile.securitypay.pay\"&payment_type=\"1\"&_input_charset=\"utf-8\"&it_b_pay=\"30m\"&success=\"true\"&sign_type=\"RSA\"&sign=\"aH560tPSQDUcB1kpXY/VNNboYG1lM8bwNuC0Td0/fY6tA09Yu+islEC97nzjB+DWDMTDn8STl+Z1K1BZY98CqR67q/UY7cm675LffieehTkU6F+rl5T7c+TBXQMZUtQQT+zFNJqRO8fdAyNIhuEfPCDxuTTUWp2eg9vj5bHVN48=\"";
resultStatus = 9000;
orderString 为以下的请求参数:
partner="2088101568358171"&seller_id="xxx@alipay.com"&out_trade_no="0819145412-6177"&subject="测试"&body="测试测试"&total_fee="0.01"¬ify_url="http://notify.msp.hk/notify.htm"&service="mobile.securitypay.pay"&payment_type="1"&_input_charset="utf-8"&it_b_pay="30m"&sign="lBBK%2F0w5LOajrMrji7DUgEqNjIhQbidR13GovA5r3TgIbNqv231yC1NksLdw%2Ba3JnfHXoXuet6XNNHtn7VE%2BeCoRO1O%2BR1KugLrQEZMtG5jmJIe2pbjm%2F3kb%2FuGkpG%2BwYQYI51%2BhA3YBbvZHVQBYveBqK%2Bh8mUyb7GM1HxWs9k4%3D"&sign_type=“RSA"
sign在服务端签名时,若服务端为java版 那么服务端要用PKCS8格式私钥来签名,而不是RSA私钥来签名否则签名字符串为nil
服务端签名时支付宝提供的工具类 还需要urlEncodedString 这是支付宝的一大坑
因为iOS端signString:方法中会urlEncodedString
//获取私钥并将商户信息签名,外部商户可以根据情况存放私钥和签名,只需要遵循RSA签名规范,并将签名字符串base64编码和UrlEncode
id<DataSigner> signer =CreateRSADataSigner(ALIPAY_PRIVATE_KEY);
NSString *signedString = [signersignString:orderSpec];
signString方法:
//该签名方法仅供参考,外部商户可用自己方法替换
- (NSString *)signString:(NSString *)string {
//在Document文件夹下创建私钥文件
NSString * signedString =nil;
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES)objectAtIndex:0];
NSString *path = [documentPathstringByAppendingPathComponent:@"AlixPay-RSAPrivateKey"];
//
// 把密钥写入文件
//
NSString *formatKey = [selfformatPrivateKey:_privateKey];
[formatKey writeToFile:pathatomically:YESencoding:NSUTF8StringEncodingerror:nil];
constchar *message = [stringcStringUsingEncoding:NSUTF8StringEncoding];
NSLog(@"%s",message);
int messageLength = (int)strlen(message);
unsignedchar *sig = (unsignedchar *)malloc(256);
unsignedint sig_len;
int ret =rsa_sign_with_private_key_pem((char *)message, messageLength, sig, &sig_len, (char *)[pathUTF8String]);
//签名成功,需要给签名字符串base64编码和UrlEncode,该两个方法也可以根据情况替换为自己函数
if (ret ==1) {
NSString *base64String =base64StringFromData([NSDatadataWithBytes:siglength:sig_len]);
//NSData * UTF8Data = [base64String dataUsingEncoding:NSUTF8StringEncoding];
NSLog(@"%@",base64String);
signedString = [selfurlEncodedString:base64String];//一定要注意这里有UrlEncode
}
free(sig);
return signedString;
}
注意:如果订单号固定 也就是每个字符串固定那么 signedString一定是一样的 而支付宝服务端的签名只是做到了 base64String这一步 ,而没有走urlEncodedString这一步,需要手动添加代码实现UrlEncode处理
同步通知客户端返回码
返回码 | 含义 |
9000 | 订单支付成功 |
8000 | 正在处理中 |
4000 | 订单支付失败 |
6001 | 用户中途取消 |
6002 | 网络连接出错 |
异步通知交易状态
枚举名称 | 枚举说明 |
WAIT_BUYER_PAY | 交易创建,等待买家付款。 |
TRADE_CLOSED | 在指定时间段内未支付时关闭的交易; 在交易完成全额退款成功时关闭的交易。 |
TRADE_SUCCESS | 交易成功,且可对该交易做操作,如:多级分润、退款等。 |
TRADE_FINISHED | 交易成功且结束,即不可再做任何操作。 |
服务器异步通知参数说明
https://doc.open.alipay.com/doc2/detail.htm?spm=a219a.7629140.0.0.oUnx1f&treeId=59&articleId=103666&docType=1以下为不可空的字符串
notify_time | 通知时间 | Date | 通知的发送时间。格式为yyyy-MM-dd HH:mm:ss。 | 不可空 | 2013-08-22 14:45:24 |
notify_type | 通知类型 | String | 通知的类型。 | 不可空 | trade_status_sync |
notify_id | 通知校验ID | String | 通知校验ID。 | 不可空 | 64ce1b6ab92d00ede0ee56ade98fdf2f4c |
sign_type | 签名方式 | String | 固定取值为RSA。 | 不可空 | RSA |
sign | 签名 | String | 请参见签名机制。 | 不可空 | lBBK%2F0w5LOajrMrji7DUgEqNjIhQbidR13GovA5r3TgIbNqv231yC1NksLdw%2Ba3JnfHXoXuet6XNNHtn7VE%2BeCoRO1O%2BR1KugLrQEZMtG5jmJI |
trade_no | 支付宝交易号 | String(64) | 该交易在支付宝系统中的交易流水号。最短16位,最长64位。 | 不可空 | 2013082244524842 |
trade_status | 交易状态 | String | 交易状态,取值范围请参见“交易状态”。 | 不可空 | TRADE_SUCCESS |
seller_id | 卖家支付宝用户号 | String(30) | 卖家支付宝账号对应的支付宝唯一用户号。以2088开头的纯16位数字。 | 不可空 | 2088501624816263 |
seller_email | 卖家支付宝账号 | String(100) | 卖家支付宝账号,可以是email和手机号码。 | 不可空 | xxx@alipay.com |
buyer_id | 买家支付宝用户号 | String(30) | 买家支付宝账号对应的支付宝唯一用户号。以2088开头的纯16位数字。 | 不可空 | 2088602315385429 |
buyer_email | 买家支付宝账号 | String(100) | 买家支付宝账号,可以是Email或手机号码。 | 不可空 | dlwdgl@gmail.com |
total_fee | 交易金额 | Number | 该笔订单的总金额。请求时对应的参数,原样通知回来。 | 不可空 | 1.00 |
为了demo方便请求参数拼装和加签都放在了客户端, 应该都放服务端
各个参数说明
partner //签约的支付宝账号对应的支付宝唯一用户号。以2088开头的16位纯数字组成。
seller_id //卖家支付宝账号(邮箱或手机号码格式)或其对应的支付宝唯一用户号(以2088开头的纯16位数字)
out_trade_no //商户网站唯一订单号:支付宝合作商户网站唯一订单号 订单号可以采用自己的订单号生成规则 这个订单号并不是支付的流水号
subject 商品名称累加中间用逗号隔开,如果一个商品买了几份只显示这一个名字 比如 商品A,商品A,商品B 应该写成 商品A,商品B//商品名称:商品的标题/交易标题/订单标题/订单关键字等。该参数最长为128个汉字****
body 商品描述累加中间用分号隔开 //String(512) 该参数最长512个汉字 商品详情:对一笔交易的具体描述信息。如果是多种商品,请将商品描述字符串累加传给body*******
total_fee %.2f保留2位小数 //总金额:该笔订单的资金总额,单位为RMB-Yuan。取值范围为[0.01,100000000.00],精确到小数点后两位
notify_url //服务器异步通知页面路径:支付宝服务器主动通知商户网站里指定的页面http路径
service //接口名称,固定值。 移动支付为 mobile.securitypay.pay
payment_type //支付类型。默认值为:1(商品购买)
_input_charset //商户网站使用的编码格式,固定为utf-8
it_b_pay //未付款交易的超时时间 //30m 代表30分钟,可空
sign 服务端签名
sign_type "RSA"
-(void)alipayOrder{
Order *order = [[Orderalloc]init];
order.partner =ALIPAY_PID;
order.seller =ALIPAY_SELLER;
order.tradeNO =@"20160503203040";//[order generateTradeNO];
NSMutableString* productName = [NSMutableStringnew];
NSMutableString* productDes = [NSMutableStringnew];
for (NSDictionary *datainself.dataSource) {
NSDictionary *item = data[SHOPPING_CAR_PRODUCT_INFO];
NSString *productNameStr = [NSStringstringWithFormat:@"%@", item[SHOPPING_CAR_PRODUCT_INFO_NAME]];
productName = [NSMutableStringstringWithFormat:@"%@,%@", productName, productNameStr];
NSString *productDesStr = [NSStringstringWithFormat:@"%@", item[SHOPPING_CAR_PRODUCT_INFO_DES]];
productDes = [NSMutableStringstringWithFormat:@"%@;%@", productDes, productDesStr];
}
NSString * subjects = [NSStringstringWithString:productName];
if (productName.length >128) {
//substringToIndex表示从第一个字符串开始截取,到指定长度位置,但是不包括该指定位置位置字符串
subjects = [subjects substringToIndex:128];
//substringFromIndex //表示从指定位置开始截取字符串到最后,所截取位置包含该指定位置
}
subjects = [subjects substringFromIndex:1];//去掉首字符逗号
NSString * bodys = [NSStringstringWithString:productDes];
if (productDes.length >512) {
bodys = [bodys substringToIndex:512];
}
bodys = [bodys substringFromIndex:1];//去掉首字符分号
//商品的标题/交易标题/订单标题/订单关键字等。该参数最长为128个汉字。
order.productName = subjects;
//String(512):对一笔交易的具体描述信息。如果是多种商品,请将商品描述字符串累加传给body。
order.productDescription = bodys;
//该笔订单的资金总额,单位为RMB-Yuan。取值范围为[0.01,100000000.00],精确到小数点后两位。
order.amount =@"0.01";//[NSString stringWithFormat:@"%.2ld",(long)[self computeTotalPrice]];//保留2位小数
order.notifyURL =ALIPAY_NOTIFY_URL;
order.service =@"mobile.securitypay.pay";
order.paymentType =@"1";
order.inputCharset =@"utf-8";
order.itBPay =@"30m";
//应用注册scheme,在Info.plist定义URL types
NSString *appScheme =APPSCHEME;
//将商品信息拼接成字符串
NSString *orderSpec = [orderdescription];
NSLog(@"orderSpec = %@",orderSpec);
//获取私钥并将商户信息签名,外部商户可以根据情况存放私钥和签名,只需要遵循RSA签名规范,并将签名字符串base64编码和UrlEncode
id<DataSigner> signer =CreateRSADataSigner(ALIPAY_PRIVATE_KEY);
NSString *signedString = [signersignString:orderSpec];
// signedString = [self urlEncodedString:@"lTP/kWT47jcCl8nFRynsHqvxoBBQ+RDAW+L7Zc0cIWEJdGjFrmrQsP5aSEiNtDTSLQa6/uVC3XaSgkFI+eN0xqmyFwAjBWFUnpx4a6HgSLLL+3X/1czh7dg4/Vq7Q2uRfYzPm5iZscP+QNqTc62JxRKKs2AIDylzZS6z5bNjXF8="] ;
// NSLog(@"signedString = %@",signedString);
//将签名成功字符串格式化为订单字符串,请严格按照该格式
NSString *orderString =nil;
if (signedString !=nil) {
orderString = [NSStringstringWithFormat:@"%@&sign=\"%@\"&sign_type=\"%@\"",
orderSpec, signedString, @"RSA"];
NSLog(@"orderString = %@",orderString);
[[AlipaySDKdefaultService]payOrder:orderStringfromScheme:appSchemecallback:^(NSDictionary *resultDic) {
//【callback处理支付结果】
NSLog(@"reslut = %@",resultDic);
}];
}
}
代码中:
ALIPAY_PID
ALIPAY_SELLER
ALIPAY_NOTIFY_URL
APPSCHEME
ALIPAY_PRIVATE_KEY
为我自己的字符串宏替换为你们自己对应的字符串
同步通知参数说明
支付宝对商户的请求数据处理完成后,会将处理的结果数据直接通知给商户。这些处理结果数据就是同步通知参数。
同步返回的数据,对于商户在服务端没有收到异步通知的时候,可以依赖服务端对同步返回的结果来进行判断是否支付成功。同步返回的结果中,sign字段描述了请求的原始数据和服务端支付的状态一起拼接的签名信息。验证这个过程包括两个部分:1、原始数据是否跟商户请求支付的原始数据一致(必须验证这个);2、验证这个签名是否能通过。上述1、2通过后,在sign字段中success=true才是可信的。【特别注意,同步结果校验的逻辑,必须放在服务端处理,切记不要放在客户端】【强烈建议商户直接依赖服务端的异步通知,忽略同步返回】
说明:
这里的规则仅限于接入sdk支付模式,对于纯粹的WAP浏览器接入的模式不适用,也就是只针对签约mobile.securitypay.pay这个服务的业务适用,浏览器模式,建议走异步通知的方式。
代码示例运行逻辑
注意:
由于在跳转支付宝客户端支付的过程中,商户app在后台很可能被系统kill了,所以pay接口的callback就会失效,请商户对standbyCallback返回的回调结果进行处理;
同步返回数据时,建议通过服务端的验签功能代码做验签处理,之后再对返回的数据做业务逻辑处理;
须以服务器异步通知的结果数据为准,并对其做业务逻辑处理;
SDK付款有两种模式:如果外部存在支付宝钱包,则直接跳转到支付宝钱包付款;不存在的场景下,在SDK内部进行H5支付。测试同学需要关注这两类测试场景。
以下信息请忽略:
此类接口网关一般为mapi(https://mapi.alipay.com/gateway.do)
在开放平台创建的应用,每个应用都有自己的应用密钥(RSA),在接口网关为openapi(https://openapi.alipay.com/gateway.do)时,需要使用应用密钥。
由商户密钥与支付宝密钥交换后与支付宝商户标识(如partnerID、APPID等)绑定。