Demo
demo是使用FMDB保存到本地(也可以使用歸檔等方法保存)
支付測試必須要使用真機
注意:在調(diào)用支付的過程比較慢,需要加入動畫提示,不然可能被拒。
內(nèi)購申請
進入https://appstoreconnect.apple.com

1.png
這個是填寫銀行卡等信息的(這里我已經(jīng)填寫了)、具體就不展示了

2.png
添加上面的信息了、需要等待蘋果審核(最晚一天、如果一天還沒有通過。請檢查個人信息)
進入我的App添加內(nèi)購項目

3.png
這里我選擇消耗型的項目作為Demo

4.png
進入用戶和訪問、添加沙盒測試員

5.png
沙盒測試

5.jpeg
內(nèi)購流程

內(nèi)購流程
根據(jù)上圖可以知道蘋果內(nèi)購結(jié)果回調(diào)是APP告訴應(yīng)用服務(wù)器的。不是蘋果服務(wù)器直接告訴應(yīng)用服務(wù)器。這樣可能出現(xiàn)漏單的情況。
比如說
網(wǎng)絡(luò)不好的說話、沒有告訴應(yīng)用服務(wù)器結(jié)果
還沒有應(yīng)用服務(wù)器結(jié)果、用戶關(guān)閉了APP等等情況。
處理方法
1.當支付成功的時候、把訂單數(shù)據(jù)保存到本地。重新進入App的時候請求支付回調(diào)
//實現(xiàn)第一部的時候可以處理大部分漏單情況、但是還是沒有辦法避免漏單(用戶刪除了APP沒有本地數(shù)據(jù)的情況等)
2.在創(chuàng)建預(yù)訂單的時候保存下單時間等數(shù)據(jù)、可以讓客服查詢
Objc
#import "ViewController.h"
#import "SqlManager.h"
#import "ApplePayModel.h"
#import <StoreKit/StoreKit.h>
@interface ViewController ()<SKPaymentTransactionObserver,SKProductsRequestDelegate>
@property (nonatomic, strong) SKProductsRequest * request;
@end
@implementation ViewController
- (void)dealloc {
NSLog(@"釋放充值");
if (self.request)
{
[self.request cancel];
}
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
}
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *array = [[SqlManager sharedManager] getAllApplePayModel];
NSLog(@"數(shù)據(jù)庫里面的數(shù)據(jù) = %@",array);
if (array.count > 0) {
for (ApplePayModel *model in array) {
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:0];
[dict setObject:model.payData forKey:@"receiptData"];
/**
* 內(nèi)購校驗的Api(你的應(yīng)用的api)
*/
}
}
}
# pragma mark 購買
- (IBAction)buyBtn:(id)sender {
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
if([SKPaymentQueue canMakePayments]){
// 你的itunesConnect的商品ID
[self getProductInfowithprotectId:@""];
}else{
NSLog(@"不允許程序內(nèi)付費");
}
}
#pragma mark -- 蘋果內(nèi)購服務(wù),下面的ProductId應(yīng)該是事先在itunesConnect中添加好的,已存在的付費項目。否則查詢會失敗。
- (void)getProductInfowithprotectId:(NSString *)proId
{
NSMutableArray *proArr = [NSMutableArray new];
[proArr addObject:proId];
NSSet * set = [NSSet setWithArray:proArr];
self.request = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
self.request.delegate = self;
[self.request start];
NSLog(@"%@",set);
NSLog(@"請求開始請等待...");
}
//收到產(chǎn)品返回信息
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
NSLog(@"--------------收到產(chǎn)品反饋消息---------------------");
NSArray *product = response.products;
NSLog(@"productID:%@", response.invalidProductIdentifiers);
if(product.count==0){
NSLog(@"查找不到商品信息");
return;
}
SKProduct *p = nil;
for(SKProduct *pro in product) {
NSLog(@"%@", [pro description]);
NSLog(@"%@", [pro localizedTitle]);
NSLog(@"%@", [pro localizedDescription]);
NSLog(@"%@", [pro price]);
NSLog(@"%@", [pro productIdentifier]);
if([pro.productIdentifier isEqualToString: [pro productIdentifier]]){
p = pro;
}
}
SKPayment *payment = [SKPayment paymentWithProduct:p];
NSLog(@"發(fā)送購買請求");
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
//請求失敗
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error{
NSLog(@"支付失敗");
}
- (void)requestDidFinish:(SKRequest *)request{
NSLog(@"支付調(diào)用完成");
}
//監(jiān)聽購買結(jié)果
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transaction{
for(SKPaymentTransaction *tran in transaction){
switch(tran.transactionState) {
case SKPaymentTransactionStatePurchased:{ // 購買成功,此時要提供給用戶相應(yīng)的內(nèi)容
[[SKPaymentQueue defaultQueue] finishTransaction:tran];
NSLog(@"購買成功 = %@", tran);
NSURL *receiptUrl = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receiptData = [NSData dataWithContentsOfURL:receiptUrl];
NSString *receiptString = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
// 保存訂單信息到數(shù)據(jù)庫
ApplePayModel *model = [ApplePayModel new];
// 這里把訂單ID寫死了
model.orderId = @"12345";
model.payData = receiptString;
[[SqlManager sharedManager] saveApplePayModel:model];
/**
* 請求校驗API 請求完成刪除數(shù)據(jù)庫對應(yīng)數(shù)據(jù)
*/
// [[SqlManager sharedManager] removeMotionModel:model];
}
break;
case SKPaymentTransactionStatePurchasing: // 購買中,此時可更新UI來展現(xiàn)購買的過程
break;
case SKPaymentTransactionStateRestored:{ //恢復(fù)已購產(chǎn)品,此時需要將已經(jīng)購買的商品恢復(fù)給用戶
[[SKPaymentQueue defaultQueue] finishTransaction:tran];
}
break;
case SKPaymentTransactionStateFailed:{ // 購買錯誤,此時要根據(jù)錯誤的代碼給用戶相應(yīng)的提示
[[SKPaymentQueue defaultQueue] finishTransaction:tran];
NSLog(@"購買失敗");
}
break;
default:
break;
}
}
}
@end
Swift
import UIKit
import StoreKit
class ViewController: UIViewController {
var request:SKProductsRequest! = nil
override func viewDidLoad() {
super.viewDidLoad()
let array = SqlManager.shared().getAllApplePayModel()
if array.count > 0 {
for model in array {
let mo:ApplePayModel = model as! ApplePayModel
print(mo.orderId as Any)
/**
* 內(nèi)購校驗的Api(你的應(yīng)用的api)
*/
}
}
}
deinit {
print("釋放充值")
if (self.request != nil) {
self.request.cancel()
}
NotificationCenter.default.removeObserver(self)
SKPaymentQueue.default().remove(self)
}
// MARK:-購買
@IBAction func buyBtn(_ sender: Any) {
SKPaymentQueue.default().add(self)
if SKPaymentQueue.canMakePayments() {
// 你的itunesConnect的商品ID
self.getProductInfow(proId: "")
}else{
print("不允許程序內(nèi)付費")
}
}
}
extension ViewController: SKProductsRequestDelegate, SKPaymentTransactionObserver{
// 蘋果內(nèi)購服務(wù),下面的ProductId應(yīng)該是事先在itunesConnect中添加好的,已存在的付費項目。否則查詢會失敗。
func getProductInfow(proId:String){
let proArr = NSMutableArray()
proArr.add(proId)
let set = NSSet(array: proArr as! [Any])
self.request = SKProductsRequest(productIdentifiers: set as! Set<String>)
self.request.delegate = self
self.request.start()
print("set")
print("請求開始請等待...")
}
// 收到產(chǎn)品返回信息
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
print("--------------收到產(chǎn)品反饋消息---------------------")
let product = response.products;
print("productID:\(response.invalidProductIdentifiers)")
if product.count == 0 {
print("查找不到商品信息")
return
}
var p = SKProduct()
for pro in product {
print(pro.description)
print(pro.localizedTitle)
print(pro.localizedDescription)
print(pro.price)
print(pro.productIdentifier)
if pro.productIdentifier == pro.productIdentifier {
p = pro
}
let payment = SKPayment(product: p)
print("發(fā)送購買請求")
SKPaymentQueue.default().add(payment)
}
}
func request(_ request: SKRequest, didFailWithError error: Error) {
print("請求失敗")
}
func requestDidFinish(_ request: SKRequest) {
print("支付調(diào)用完成")
}
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for tran in transactions {
switch tran.transactionState {
case .purchased: // 購買成功,此時要提供給用戶相應(yīng)的內(nèi)容
SKPaymentQueue.default().finishTransaction(tran)
let model = ApplePayModel()
model.orderId = "1234"
model.payData = "1234"
SqlManager.shared().saveApplePayModel(model: model)
/**
* 請求校驗API 請求完成刪除數(shù)據(jù)庫對應(yīng)數(shù)據(jù)
*/
// SqlManager.shared().removeMotionModel(model: model)
break
case .purchasing: // 購買中,此時可更新UI來展現(xiàn)購買的過程
break
case .restored: // 恢復(fù)已購產(chǎn)品,此時需要將已經(jīng)購買的商品恢復(fù)給用戶
SKPaymentQueue.default().finishTransaction(tran)
break
case .failed: // 購買錯誤,此時要根據(jù)錯誤的代碼給用戶相應(yīng)的提示
SKPaymentQueue.default().finishTransaction(tran)
print("購買失敗")
break
default:
break
}
}
}
}
Demo
demo是使用FMDB保存到本地(也可以使用歸檔等方法保存)
如果要保存蘋果內(nèi)購數(shù)據(jù)還是使用keychain保存的好
keyChain是ios中唯一可以存儲安全可靠敏感數(shù)據(jù)的地方。而且應(yīng)用被卸載,數(shù)據(jù)也不會被刪除,所以非常可靠。
keychain文章