fix: Add transaction details to StoreKit callbacks

This commit is contained in:
Yaroslav Yashin 2025-06-16 19:05:18 +03:00
parent 95aad7ac82
commit 15607f0beb
4 changed files with 63 additions and 28 deletions

View file

@ -12,7 +12,10 @@
+ (instancetype)sharedInstance; + (instancetype)sharedInstance;
- (void)purchaseProduct:(NSString *)productIdentifier - (void)purchaseProduct:(NSString *)productIdentifier
completion:(void (^)(BOOL success, NSError *_Nullable error))completion; completion:(void (^)(BOOL success,
NSString *_Nullable transactionId,
NSString *_Nullable productId,
NSError *_Nullable error))completion;
- (void)restorePurchasesWithCompletion:(void (^)(BOOL success, NSError *_Nullable error))completion; - (void)restorePurchasesWithCompletion:(void (^)(BOOL success, NSError *_Nullable error))completion;

View file

@ -6,7 +6,10 @@
#import <StoreKit/StoreKit.h> #import <StoreKit/StoreKit.h>
@interface StoreKitController () <SKProductsRequestDelegate, SKPaymentTransactionObserver> @interface StoreKitController () <SKProductsRequestDelegate, SKPaymentTransactionObserver>
@property (nonatomic, copy) void (^purchaseCompletion)(BOOL success, NSError *_Nullable error); @property (nonatomic, copy) void (^purchaseCompletion)(BOOL success,
NSString *_Nullable transactionId,
NSString *_Nullable productId,
NSError *_Nullable error);
@property (nonatomic, copy) void (^restoreCompletion)(BOOL success, NSError *_Nullable error); @property (nonatomic, copy) void (^restoreCompletion)(BOOL success, NSError *_Nullable error);
@property (nonatomic, strong) SKProductsRequest *productsRequest; @property (nonatomic, strong) SKProductsRequest *productsRequest;
@end @end
@ -38,7 +41,10 @@
} }
- (void)purchaseProduct:(NSString *)productIdentifier - (void)purchaseProduct:(NSString *)productIdentifier
completion:(void (^)(BOOL success, NSError *_Nullable error))completion completion:(void (^)(BOOL success,
NSString *_Nullable transactionId,
NSString *_Nullable productId,
NSError *_Nullable error))completion
{ {
self.purchaseCompletion = completion; self.purchaseCompletion = completion;
self.productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:productIdentifier]]; self.productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:productIdentifier]];
@ -62,7 +68,7 @@
NSError *error = [NSError errorWithDomain:@"StoreKitController" NSError *error = [NSError errorWithDomain:@"StoreKitController"
code:0 code:0
userInfo:@{ NSLocalizedDescriptionKey : @"Product not found" }]; userInfo:@{ NSLocalizedDescriptionKey : @"Product not found" }];
self.purchaseCompletion(NO, error); self.purchaseCompletion(NO, nil, nil, error);
self.purchaseCompletion = nil; self.purchaseCompletion = nil;
} }
return; return;
@ -75,7 +81,7 @@
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error - (void)request:(SKRequest *)request didFailWithError:(NSError *)error
{ {
if (self.purchaseCompletion) { if (self.purchaseCompletion) {
self.purchaseCompletion(NO, error); self.purchaseCompletion(NO, nil, nil, error);
self.purchaseCompletion = nil; self.purchaseCompletion = nil;
} }
self.productsRequest = nil; self.productsRequest = nil;
@ -89,7 +95,10 @@
switch (transaction.transactionState) { switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchased: case SKPaymentTransactionStatePurchased:
if (self.purchaseCompletion) { if (self.purchaseCompletion) {
self.purchaseCompletion(YES, nil); self.purchaseCompletion(YES,
transaction.transactionIdentifier,
transaction.payment.productIdentifier,
nil);
self.purchaseCompletion = nil; self.purchaseCompletion = nil;
} }
[[SKPaymentQueue defaultQueue] finishTransaction:transaction]; [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
@ -97,7 +106,10 @@
break; break;
case SKPaymentTransactionStateFailed: case SKPaymentTransactionStateFailed:
if (self.purchaseCompletion) { if (self.purchaseCompletion) {
self.purchaseCompletion(NO, transaction.error); self.purchaseCompletion(NO,
transaction.transactionIdentifier,
transaction.payment.productIdentifier,
transaction.error);
self.purchaseCompletion = nil; self.purchaseCompletion = nil;
} }
[[SKPaymentQueue defaultQueue] finishTransaction:transaction]; [[SKPaymentQueue defaultQueue] finishTransaction:transaction];

View file

@ -2,6 +2,7 @@
#define IOS_CONTROLLER_H #define IOS_CONTROLLER_H
#include "protocols/vpnprotocol.h" #include "protocols/vpnprotocol.h"
#include <functional>
#ifdef __OBJC__ #ifdef __OBJC__
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
@ -54,8 +55,13 @@ public:
bool shareText(const QStringList &filesToSend); bool shareText(const QStringList &filesToSend);
QString openFile(); QString openFile();
bool purchaseProduct(const QString &productId); void purchaseProduct(const QString &productId,
bool restorePurchases(); std::function<void(bool success,
const QString &transactionId,
const QString &purchasedProductId,
const QString &errorString)> &&callback);
void restorePurchases(std::function<void(bool success,
const QString &errorString)> &&callback);
void requestInetAccess(); void requestInetAccess();
signals: signals:

View file

@ -849,34 +849,48 @@ QString IosController::openFile() {
return filePath; return filePath;
} }
bool IosController::purchaseProduct(const QString &productId) void IosController::purchaseProduct(const QString &productId,
std::function<void(bool success,
const QString &transactionId,
const QString &purchasedProductId,
const QString &errorString)> &&callback)
{ {
__block BOOL success = NO;
StoreKitController *controller = [StoreKitController sharedInstance]; StoreKitController *controller = [StoreKitController sharedInstance];
QEventLoop wait; [controller purchaseProduct:productId.toNSString() completion:^(BOOL s,
[controller purchaseProduct:productId.toNSString() completion:^(BOOL s, NSError * _Nullable error) { NSString * _Nullable transactionId,
Q_UNUSED(error); NSString * _Nullable prodId,
success = s; NSError * _Nullable error) {
emit finished(); QString txId;
QString pId;
QString err;
if (transactionId) {
txId = QString::fromUtf8(transactionId.UTF8String);
}
if (prodId) {
pId = QString::fromUtf8(prodId.UTF8String);
}
if (error) {
err = QString::fromUtf8(error.localizedDescription.UTF8String);
}
if (callback) {
callback(s, txId, pId, err);
}
}]; }];
QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit);
wait.exec();
return success;
} }
bool IosController::restorePurchases() void IosController::restorePurchases(std::function<void(bool success,
const QString &errorString)> &&callback)
{ {
__block BOOL success = NO;
StoreKitController *controller = [StoreKitController sharedInstance]; StoreKitController *controller = [StoreKitController sharedInstance];
QEventLoop wait;
[controller restorePurchasesWithCompletion:^(BOOL s, NSError * _Nullable error) { [controller restorePurchasesWithCompletion:^(BOOL s, NSError * _Nullable error) {
Q_UNUSED(error); QString err;
success = s; if (error) {
emit finished(); err = QString::fromUtf8(error.localizedDescription.UTF8String);
}
if (callback) {
callback(s, err);
}
}]; }];
QObject::connect(this, &IosController::finished, &wait, &QEventLoop::quit);
wait.exec();
return success;
} }
void IosController::requestInetAccess() { void IosController::requestInetAccess() {