- 一句話集成微信支付,支付寶支付以及內購
可以在github上下載的工具類
- 如何使用
1.將工具類拖入工程
2.AppDelegate代碼如下:
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
return [[XNPayManager sharedPayManager] handleOpenURL:url];
}
// NOTE: 9.0以后使用新API接口
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString*, id> *)options
{
return [[XNPayManager sharedPayManager] handleOpenURL:url];
}
3.在使用的地方調起微信或者支付寶
- 支付寶
TIPS:拼裝參數應在商戶服務端完成,商戶服務端直接將組裝和簽名后的請求串orderString傳給客戶端,客戶端直接傳給SDK發起請求。文檔和Demo是為了示例效果在客戶端實現。所以我們項目中應該按照次操作
// params應該是由后臺返回的
[[XNPayManager sharedPayManager] ALIPayWithPayParams: params success:^(PayCode code) {
// 成功業務邏輯
DLog(@"成功");
} failure:^(PayCode code) {
// 失敗業務邏輯
DLog(@"失敗");
}]
- 微信支付
// dict應該是由后臺返回的
[[XNPayManager sharedPayManager] WXPayWithPayDict: dict success:^(PayCode code) {
// 成功業務邏輯
DLog(@"成功");
} failure:^(PayCode code) {
// 失敗業務邏輯
DLog(@"成功");
}];
- 內購
//productId 產品唯一標示
[[XNPayManager sharedPayManager] requestProductData: productId success:^(PayCode code) {
// 成功業務邏輯
} failure:^(PayCode code) {
// 失敗業務邏輯
}];
- 詳細代碼
在封裝的代碼中沒有使用通知,因為通知是全局的,然后在項目中出現了一下問題,比如:待支付訂單->訂單詳情 這兩個頁面都有支付的操作.如果使用通知:這兩個控制器支付成功\失敗的業務邏輯就會沖突
1.聲明支付單例
+ (instancetype)sharedPayManager {
static dispatch_once_t onceToken;
static XNPayManager *instance;
dispatch_once(&onceToken, ^{
instance = [[XNPayManager alloc] init];
});
return instance;
}
2.自定義支付狀態碼,其實也可以不用定義
/**
支付狀態碼
*/
typedef NS_ENUM(NSInteger, PayCode) {
WXSUCESS = 1001, /**成功*/
WXERROR = 1002, /**失敗*/
WXSCANCEL = 1003, /**取消*/
ALIPAYSUCESS = 1101, /**支付寶支付成功*/
ALIPAYERROR = 1102, /**支付寶支付錯誤*/
ALIPAYCANCEL = 1103, /**支付寶支付取消*/
APPSTOREPAYSUCESS = 1201, /**內購支付成功*/
APPSTOREPAYERROR = 1201, /**內購支付成功*/
APPSTOREPAYCANCEL = 1201, /**內購支付成功*/
};
3.微信支付
/**
微信支付
@param dict 微信訂單字典(全部由后臺拼接好給iOS端)
@param successBlock 成功的回調
@param failBolck 失敗的回調
*/
- (void)WXPayWithPayDict:(NSDictionary *)dict
success:(void(^)(PayCode code)) successBlock
failure:(void(^)(PayCode code)) failBolck {
self.successBlock = successBlock;
self.failBolck = failBolck;
NSString *strMsg = nil;
//1. 判斷是否安裝微信
if (![WXApi isWXAppInstalled]) {
NSLog(@"您尚未安裝\"微信App\",請先安裝后再返回支付");
strMsg = @"您尚未安裝\"微信App\",請先安裝后再返回支付";
[self tipMessageAlert:nil message:strMsg];
return;
}
//2.判斷微信的版本是否支持最新Api
if (![WXApi isWXAppInstalled]) {
NSLog(@"您微信當前版本不支持此功能,請先升級微信應用");
strMsg = @"您微信當前版本不支持此功能,請先升級微信應用";
[self tipMessageAlert:nil message:strMsg];
return;
}
if (!kDictIsEmpty(dict)) {
//調起微信支付
PayReq *req = [[PayReq alloc]init];
req.openID = dict[@"appid"];
req.partnerId = dict[@"partnerid"];
req.prepayId = dict[@"prepayid"];
req.nonceStr = dict[@"noncestr"];
req.timeStamp = [dict[@"timestamp"] intValue];
req.package = @"Sign=WXPay";
req.sign = dict[@"sign"];
[WXApi sendReq:req];
}
}
#pragma mark - WXApiDelegate
//支付結果回調
/// - see [支付結果回調](https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_5)
- (void)onResp:(BaseResp *)resp {
NSString *strMsg = [NSString stringWithFormat:@"errcode:%d",resp.errCode];
//回調中errCode值列表:
// 0 成功 展示成功頁面
//-1 錯誤 可能的原因:簽名錯誤、未注冊APPID、項目設置APPID不正確、注冊的APPID與設置的不匹配、其他異常等
//-2 用戶取消 無需處理。發生場景:用戶不支付了,點擊取消,返回APP
if ([resp isKindOfClass:[PayResp class]]) {
// 支付返回結果,實際支付結果需要去微信服務器端查詢
switch (resp.errCode) {
case WXSuccess:{
strMsg = @"支付結果:成功!";
self.successBlock(WXSUCESS);
DLog(@"支付成功-PaySuccess,retcode = %d", resp.errCode);
break;
}
case WXErrCodeUserCancel:{
strMsg = @"支付結果:取消";
self.failBolck(WXSCANCEL);
DLog(@"支付取消-PayCancel,retcode = %d", resp.errCode);
}
default:{
strMsg = @"支付結果:失敗";
self.failBolck(WXERROR);
DLog(@"錯誤,retcode = %d, retstr = %@", resp.errCode,resp.errStr);
break;
}
}
[self tipMessageAlert:@"支付結果" message:strMsg];
}
}
4.支付寶
/**
支付寶支付
@param params 支付寶支付參數(全部由后臺拼接好給iOS端)
@param successBlock 成功的回調
@param failBolck 失敗的回調
*/
- (void)ALIPayWithPayParams:(NSString *)params
success:(void(^)(PayCode code)) successBlock
failure:(void(^)(PayCode code)) failBolck {
self.successBlock = successBlock;
self.failBolck = failBolck;
NSString *appScheme = @"appScheme";
[[AlipaySDK defaultService] payOrder:params fromScheme:appScheme callback:^(NSDictionary *resultDic) {
DLog(@"我這里是payVC%@",resultDic);
DLog(@"%@",resultDic[@"memo"]);
[self aliPayResult:resultDic];
}];
}
#pragma mark - 支付寶支付結果處理
- (void)aliPayResult:(NSDictionary *)resultDic {
// 返回結果需要通過 resultStatus 以及 result 字段的值來綜合判斷并確定支付結果。 在 resultStatus=9000,并且 success="true"以及 sign="xxx"校驗通過的情況下,證明支付成功。其它情況歸為失敗。較低安全級別的場合,也可以只通過檢查 resultStatus 以及 success="true"來判定支付結果
NSInteger resultStatus = [resultDic[@"resultStatus"] integerValue];
if (resultDic && [resultDic objectForKey:@"resultStatus"]) {
switch (resultStatus) {
case 9000:
[self tipMessageAlert:@"支付結果" message:@"訂單支付成功"];
self.successBlock(ALIPAYSUCESS);
break;
case 8000:
[self tipMessageAlert:@"支付結果" message:@"正在處理中"];
self.failBolck(ALIPAYERROR);
break;
case 4000:
[self tipMessageAlert:@"支付結果" message:@"訂單支付失敗,請稍后再試"];
self.failBolck(ALIPAYERROR);
break;
case 6001:
[self tipMessageAlert:@"支付結果" message:@"已取消支付"];
self.failBolck(ALIPAYCANCEL);
break;
case 6002:
[self tipMessageAlert:@"支付結果" message:@"網絡連接錯誤,請稍后再試"];
self.failBolck(ALIPAYERROR);
break;
default:
break;
}
}
}
5.內購
/**
內購
@param productId productId
@param successBlock 成功的回調
@param failBolck 失敗的回調
*/
- (void)requestProductData:(NSString *)productId
success:(void(^)(PayCode code)) successBlock
failure:(void(^)(PayCode code)) failBolck {
if ([SKPaymentQueue canMakePayments]) {
self.successBlock = successBlock;
self.failBolck = failBolck;
// 最好設置上
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:kKeyWindow animated:YES];
hud.labelText = @"正在購買,請勿離開...";
hud.dimBackground = YES;
self.productId = productId;
NSArray *productArr = [[NSArray alloc]initWithObjects:productId, nil];
NSSet *productSet = [NSSet setWithArray:productArr];
SKProductsRequest *request = [[SKProductsRequest alloc]initWithProductIdentifiers:productSet];
request.delegate = self;
[request start];
}else{
DLog(@"不允許程序內付費");
}
}
#pragma mark - SKProductsRequestDelegate
// 收到產品返回信息
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSArray *productArr = response.products;
if ([productArr count] == 0) {
DLog(@"沒有該商品");
[MBProgressHUD hideAllHUDsForView:kKeyWindow animated:YES];
return;
}
DLog(@"productId = %@",response.invalidProductIdentifiers);
DLog(@"產品付費數量 = %zd",productArr.count);
SKProduct *p = nil;
for (SKProduct *pro in productArr) {
DLog(@"description:%@",[pro description]);
DLog(@"localizedTitle:%@",[pro localizedTitle]);
DLog(@"localizedDescription:%@",[pro localizedDescription]);
DLog(@"price:%@",[pro price]);
DLog(@"productIdentifier:%@",[pro productIdentifier]);
if ([pro.productIdentifier isEqualToString:self.productId]) {
p = pro;
}
}
SKPayment *payment = [SKPayment paymentWithProduct:p];
//發送內購請求
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
#pragma mark - SKRequestDelegate
- (void)requestDidFinish:(SKRequest *)request {
DLog(@"獲取產品成功");
// [MBProgressHUD hideHUDForView:kKeyWindow animated:YES];
}
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
[MBProgressHUD hideHUDForView:kKeyWindow animated:YES];
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:kKeyWindow animated:YES];
hud.labelText = @"購買失敗";
hud.mode = MBProgressHUDModeText;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView:kKeyWindow animated:YES];
});
}
// 監聽購買結果
//SKPaymentTransactionObserver
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions {
for (SKPaymentTransaction *tran in transactions) {
switch (tran.transactionState) {
case SKPaymentTransactionStatePurchased: //交易完成
[self completeTransaction:tran];
// Remove the transaction from the payment queue.
[[SKPaymentQueue defaultQueue] finishTransaction: tran];
break;
case SKPaymentTransactionStatePurchasing: //商品添加進列表
break;
case SKPaymentTransactionStateRestored: //購買過
[self restoreTransaction:tran];
break;
case SKPaymentTransactionStateFailed: //交易失敗
[self failedTransaction:tran];
// Remove the transaction from the payment queue.
[[SKPaymentQueue defaultQueue] finishTransaction: tran];
break;
default:
break;
}
}
}
- (void)completeTransaction:(SKPaymentTransaction *)transaction {
NSString * productIdentifier = transaction.payment.productIdentifier;
DLog(@"productIdentifier Product id:%@", productIdentifier);
NSString *transactionReceiptString= nil;
//系統IOS7.0以上獲取支付驗證憑證的方式應該改變,切驗證返回的數據結構也不一樣了。
// 驗證憑據,獲取到蘋果返回的交易憑據
// appStoreReceiptURL iOS7.0增加的,購買交易完成后,會將憑據存放在該地址
NSURL *receiptUrl=[[NSBundle mainBundle] appStoreReceiptURL];
NSData * receiptData = [NSData dataWithContentsOfURL:receiptUrl];
transactionReceiptString = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
DLog(@"transactionReceiptString == %@",transactionReceiptString);
if ([productIdentifier length] > 0) {
self.hud.labelText = @"正在驗證,請勿離開...";
// 請求自己的服務器去驗證用戶購買結果
// 往后臺驗證,只有服務器有反應就hide
[MBProgressHUD hideAllHUDsForView:kKeyWindow animated:YES];
}
}
- (void)failedTransaction:(SKPaymentTransaction *)transaction {
if(transaction.error.code != SKErrorPaymentCancelled) {
DLog(@"購買失敗");
self.failBolck(APPSTOREPAYCANCEL);
} else {
DLog(@"用戶取消交易");
self.failBolck(APPSTOREPAYCANCEL);
}
[MBProgressHUD hideHUDForView:kKeyWindow animated:YES];
[SMGlobalMethod showViewCenter:kKeyWindow.center longMessage:@"購買失敗"];
}
- (void)restoreTransaction:(SKPaymentTransaction *)transaction {
// 對于已購商品,處理恢復購買的邏輯
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
6.單例回調
#pragma mark - 單例類回調
- (BOOL)handleOpenURL:(NSURL *)url {
if ([url.host isEqualToString:@"safepay"])
{
//跳轉支付寶錢包進行支付,處理支付結果
[[AlipaySDK defaultService] processOrderWithPaymentResult:url standbyCallback:^(NSDictionary *resultDic) {
DLog(@"result = %@",resultDic);
DLog(@"openURL : 支付寶回調 : result = %@",resultDic);
[self aliPayResult:resultDic];
}];
// 授權跳轉支付寶錢包進行支付,處理支付結果
[[AlipaySDK defaultService] processAuth_V2Result:url standbyCallback:^(NSDictionary *resultDic) {
NSLog(@"result = %@",resultDic);
// 解析 auth code
NSString *result = resultDic[@"result"];
NSString *authCode = nil;
if (result.length>0) {
NSArray *resultArr = [result componentsSeparatedByString:@"&"];
for (NSString *subResult in resultArr) {
if (subResult.length > 10 && [subResult hasPrefix:@"auth_code="]) {
authCode = [subResult substringFromIndex:10];
break;
}
}
}
DLog(@"授權結果 authCode = %@", authCode?:@"");
}];
return [url.host isEqualToString:@"safepay"];
}
else
{
return [WXApi handleOpenURL:url delegate:self];
}
}
參考博客: