今天闲着没事做,集成了一下银联支付,原来的项目中都是支付宝、微信支付。其实步骤很简单,只要配置正确了,还是挺好做的。
首先是sdk下载地址:https://open.unionpay.com/ajweb/help/file/toDetailPage?id=346&flag=2
以下是测试用卡号、手机号等信息(此类信息仅供测试使用,不会发生真实交易)
招商银行借记卡:6226090000000048 |
华夏银行贷记卡:6226388000000095 |
一,工程配置
1、 使用UPPaymentControl需要添加CFNetwork.framework、SystemConfiguration.framework、libz、libPaymentControl.a到工程中,添加后如下图:
2、在工程info.plist设置中添加一个URL Types回调协议(在UPPayDemo工程中使用“UPPayDemo”作为协议),用于在支付完成后返回商户客户端。
3、http请求设置
在Xcode7.0之后的版本中进行http请求时,需要在工程对应的plist文件中添加NSAppTransportSecurity Dictionary并同时设置里面NSAllowsArbitraryLoads 属性值为 YES,具体设置可参照以下截图:
4、添加协议白名单
在Xcode7.0之后的版本中进行开发,需要在工程对应的plist文件中,添加LSApplicationQueriesSchemes Array并加入uppaysdk、uppaywallet、uppayx1、uppayx2、uppayx3五个item,具体设置可参考以下截图:
或者直接添加如下代码到plist文件中:
<key>LSApplicationQueriesSchemes</key> <array> <string>uppaysdk</string> <string>uppaywallet</string> <string>uppayx1</string> <string>uppayx2</string> <string>uppayx3</string> </array> |
6.工程的 Build Settings 中找到 Other Linker Flags 中添加-ObjC 宏;
二,注意
银联支付目测只需要2个参数 1 tn 其实就是订单号 2 mode 是测试环境还是线上环境
和支付宝不同的是 支付宝要求本地把订单的信息书写完毕 调起sdk支付即可 银联是我把一些生成订单的信息 如 付款方 收款方 订单描述 金额等信息传递到服务器 由服务器生成一个订单号 然后客户端调起sdk进入支付
mode 是个字符串 "00" 表示线上环境"01"表示测试环境
返回的代理结果 :success、fail、cancel,分别代表:支付成功、支付失败、用户取消支付
三,集成到工程中
1,工程Appdelegate.m
#import "AppDelegate.h"
#import "UPPaymentControl.h"
#import <CommonCrypto/CommonDigest.h>
#import "RSA.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL) application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
[[UPPaymentControldefaultControl]handlePaymentResult:urlcompleteBlock:^(NSString *code,NSDictionary *data)
{
//结果code为成功时,先校验签名,校验成功后做后续处理
if ([codeisEqualToString:@"success"]) {
//数据从NSDictionary转换为NSString
NSDictionary *data;
NSData *signData = [NSJSONSerializationdataWithJSONObject:data
options:0
error:nil];
NSString *sign = [[NSStringalloc]initWithData:signDataencoding:NSUTF8StringEncoding];
//判断签名数据是否存在
if(data ==nil){
//如果没有签名数据,建议商户app后台查询交易结果
return;
}
//验签证书同后台验签证书
//此处的verify,商户需送去商户后台做验签
if([selfverify:sign]) {
//支付成功且验签成功,展示支付成功提示
}
else {
//验签失败,交易结果数据被篡改,商户app后台查询交易结果
}
}
elseif([codeisEqualToString:@"fail"]) {
//交易失败
}
elseif([codeisEqualToString:@"cancel"]) {
//交易取消
}
}];
returnYES;
}
- (NSString *) readPublicKey:(NSString *) keyName
{
if (keyName ==nil || [keyNameisEqualToString:@""])returnnil;
NSMutableArray *filenameChunks = [[keyNamecomponentsSeparatedByString:@"."]mutableCopy];
NSString *extension = filenameChunks[[filenameChunkscount] -1];
[filenameChunks removeLastObject];// remove the extension
NSString *filename = [filenameChunkscomponentsJoinedByString:@"."];// reconstruct the filename with no extension
NSString *keyPath = [[NSBundlemainBundle]pathForResource:filenameofType:extension];
NSString *keyStr = [NSStringstringWithContentsOfFile:keyPathencoding:NSUTF8StringEncodingerror:nil];
return keyStr;
}
-(BOOL) verify:(NSString *) resultStr {
//验签证书同后台验签证书
//此处的verify,商户需送去商户后台做验签
returnNO;
}
- (NSString*)sha1:(NSString *)string
{
unsignedchar digest[CC_SHA1_DIGEST_LENGTH];
CC_SHA1_CTX context;
NSString *description;
CC_SHA1_Init(&context);
memset(digest,0,sizeof(digest));
description = @"";
if (string ==nil)
{
returnnil;
}
// Convert the given 'NSString *' to 'const char *'.
constchar *str = [stringcStringUsingEncoding:NSUTF8StringEncoding];
// Check if the conversion has succeeded.
if (str ==NULL)
{
returnnil;
}
// Get the length of the C-string.
int len = (int)strlen(str);
if (len ==0)
{
returnnil;
}
if (str ==NULL)
{
returnnil;
}
CC_SHA1_Update(&context, str, len);
CC_SHA1_Final(digest, &context);
description = [NSStringstringWithFormat:
@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
digest[ 0], digest[1], digest[2], digest[3],
digest[ 4], digest[5], digest[6], digest[7],
digest[ 8], digest[9], digest[10], digest[11],
digest[12], digest[13], digest[14], digest[15],
digest[16], digest[17], digest[18], digest[19]];
return description;
}
2. 支付页面 ViewController.mm
#import "ViewController.h"
#import "UPPaymentControl.h"
#define kVCTitle @"商户测试"
#define kBtnFirstTitle @"获取订单,开始测试"
#define kWaiting @"正在获取TN,请稍后..."
#define kNote @"提示"
#define kConfirm @"确定"
#define kErrorNet @"网络错误"
#define kResult @"支付结果:%@"
//线上环境
#define kMode_Development @"01"
//在后台开发实现消费(获取tn)请求前,demo里默认由银联的一个商户仿真获取tn(http://202.101.25.178:8080/sim/gettn或http://101.231.204.84:8091/sim/getacptn)之后需要改从商户自己的后台那里获取tn的。
#define kURL_TN_Normal @"http://101.231.204.84:8091/sim/getacptn"
@interface ViewController ()
{
UITextField *_curField;
UIAlertView* _alertView;
NSMutableData* _responseData;
CGFloat _maxWidth;
CGFloat _maxHeight;
}
/** 测试环境 */
@property (copy,nonatomic)NSString *tnMode;
/** 订单 */
@property (copy,nonatomic)NSString *tn;
@end
@implementation ViewController
- (void)viewDidLoad {
[superviewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[superdidReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/** 银联支付 */
- (IBAction)unionPayBtnClick:(UIButton *)sender
{
// //支付环境: "00"表示线上环境"01"表示测试环境
// NSString *tnMode = @"01";
// //订单号
// NSString *tn = @"20161234514531";
// //当判断用户手机是否已经安装了银联APP。
// if ([[UPPaymentControl defaultControl]isPaymentAppInstalled]) {
// //商户App从商户服务器获取tn,当tn不为空时,调用支付接口
// if (tn !=nil && tn.length > 0) {
// [[UPPaymentControl defaultControl]startPay:tn fromScheme:@"UPPayDemo" mode:tnMode viewController:self];
// }
// }
self.tnMode =kMode_Development;
[selfstartNetWithURL:[NSURLURLWithString:kURL_TN_Normal]];
}
- (void)startNetWithURL:(NSURL *)url
{
[_curFieldresignFirstResponder];
_curField =nil;
[selfshowAlertWait];
NSURLRequest * urlRequest=[NSURLRequestrequestWithURL:url];
NSURLConnection* urlConn = [[NSURLConnectionalloc]initWithRequest:urlRequestdelegate:self];
[urlConn start];
}
#pragma mark - Alert
- (void)showAlertWait
{
[selfhideAlert];
_alertView = [[UIAlertViewalloc]initWithTitle:kWaitingmessage:nildelegate:selfcancelButtonTitle:nilotherButtonTitles:nil];
[_alertViewshow];
UIActivityIndicatorView* aiv = [[UIActivityIndicatorViewalloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
aiv.center =CGPointMake(_alertView.frame.size.width / 2.0f - 15,_alertView.frame.size.height / 2.0f + 10 );
[aiv startAnimating];
[_alertViewaddSubview:aiv];
}
- (void)showAlertMessage:(NSString*)msg
{
[selfhideAlert];
_alertView = [[UIAlertViewalloc]initWithTitle:kNotemessage:msgdelegate:selfcancelButtonTitle:kConfirmotherButtonTitles:nil,nil];
}
- (void)hideAlert
{
if (_alertView !=nil)
{
[_alertViewdismissWithClickedButtonIndex:0animated:NO];
_alertView =nil;
}
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
_alertView =nil;
}
#pragma mark UPPayPluginResult支付完成的返回结果
- (void)UPPayPluginResult:(NSString *)result
{
NSString* msg = [NSStringstringWithFormat:kResult, result];
[selfshowAlertMessage:msg];
}
#pragma mark - 网络请求代理方法connection
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse*)response
{
NSHTTPURLResponse* rsp = (NSHTTPURLResponse*)response;
NSInteger code = [rspstatusCode];
if (code !=200)
{
[selfshowAlertMessage:kErrorNet];
[connection cancel];
}
else
{
_responseData = [[NSMutableDataalloc]init];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[_responseDataappendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[selfhideAlert];
NSString* tn = [[NSMutableStringalloc]initWithData:_responseDataencoding:NSUTF8StringEncoding];
if (tn !=nil && tn.length >0)
{
NSLog(@"tn=%@",tn);
[[UPPaymentControldefaultControl]startPay:tnfromScheme:@"UPPayDemo"mode:self.tnModeviewController:self];
}
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
[selfshowAlertMessage:kErrorNet];
}
四、效果图