Skip to content

Commit 44fa617

Browse files
NickSxticlaude
andcommitted
refactor: use PurchaseResult instead of DeferredTransaction for deferred purchases
The Sandwich bridge emits a PurchaseResult map (not a flat DeferredTransaction). Reuse the existing PurchaseResult model for the DeferredPurchasesListener, matching the native SDK interface. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 9a5f1cb commit 44fa617

6 files changed

Lines changed: 32 additions & 110 deletions

File tree

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import DeferredTransaction from './DeferredTransaction';
1+
import PurchaseResult from './PurchaseResult';
22

33
export interface DeferredPurchasesListener {
44

55
/**
66
* Called when a deferred purchase completes.
77
* For example, when pending purchases like SCA, Ask to buy, etc., are approved and finalized.
8-
* Provides full transaction details, including for consumable products without entitlements.
9-
* @param transaction the completed deferred transaction with full details.
8+
* Provides the full purchase result with entitlements and store transaction details.
9+
* @param purchaseResult the result of the completed deferred purchase.
1010
*/
11-
onDeferredPurchaseCompleted(transaction: DeferredTransaction): void;
11+
onDeferredPurchaseCompleted(purchaseResult: PurchaseResult): void;
1212
}

src/dto/DeferredTransaction.ts

Lines changed: 0 additions & 51 deletions
This file was deleted.

src/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ export { default as QonversionConfigBuilder } from './QonversionConfigBuilder';
55

66
export type { EntitlementsUpdateListener } from './dto/EntitlementsUpdateListener';
77
export type { DeferredPurchasesListener } from './dto/DeferredPurchasesListener';
8-
export { default as DeferredTransaction } from './dto/DeferredTransaction';
98
export * from './dto/enums';
109
export { default as IntroEligibility } from './dto/IntroEligibility';
1110
export { default as Offering } from './dto/Offering';

src/internal/Mapper.ts

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ import QonversionError from '../dto/QonversionError';
5656
import NoCodesError from '../dto/NoCodesError';
5757
import PurchaseResult from '../dto/PurchaseResult';
5858
import StoreTransaction from '../dto/StoreTransaction';
59-
import DeferredTransaction from '../dto/DeferredTransaction';
6059

6160
export type QProduct = {
6261
id: string;
@@ -1252,25 +1251,6 @@ class Mapper {
12521251

12531252
// endregion
12541253

1255-
// region DeferredTransaction
1256-
1257-
static convertDeferredTransaction(
1258-
transaction: Record<string, any> | null | undefined
1259-
): DeferredTransaction | null {
1260-
if (!transaction) {
1261-
return null;
1262-
}
1263-
1264-
return new DeferredTransaction(
1265-
transaction.productId ?? '',
1266-
transaction.transactionId ?? null,
1267-
transaction.originalTransactionId ?? null,
1268-
transaction.type ?? 'Unknown',
1269-
transaction.value ?? 0,
1270-
transaction.currency ?? null
1271-
);
1272-
}
1273-
12741254
// endregion
12751255
}
12761256

src/internal/QonversionInternal.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -411,10 +411,10 @@ export default class QonversionInternal implements QonversionApi {
411411
}
412412

413413
private deferredPurchaseCompletedEventHandler = (payload: Object) => {
414-
const transaction = Mapper.convertDeferredTransaction(payload as Record<string, any>);
414+
const purchaseResult = Mapper.convertPurchaseResult(payload as Record<string, any>);
415415

416-
if (transaction) {
417-
this.deferredPurchasesListener?.onDeferredPurchaseCompleted(transaction);
416+
if (purchaseResult) {
417+
this.deferredPurchasesListener?.onDeferredPurchaseCompleted(purchaseResult);
418418
}
419419
}
420420

src/internal/__tests__/QonversionInternal.test.ts

Lines changed: 25 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import type { EntitlementsUpdateListener } from '../../dto/EntitlementsUpdateListener';
22
import type { DeferredPurchasesListener } from '../../dto/DeferredPurchasesListener';
33
import type { QEntitlement } from '../Mapper';
4-
import DeferredTransaction from '../../dto/DeferredTransaction';
4+
import PurchaseResult from '../../dto/PurchaseResult';
5+
import { PurchaseResultStatus, PurchaseResultSource } from '../../dto/enums';
56

67
// Capture event handlers registered on the native module mock
78
const eventHandlers: Record<string, Function> = {};
@@ -30,8 +31,16 @@ jest.mock('../specs/NativeQonversionModule', () => ({
3031
},
3132
}));
3233

34+
const mockPurchaseResult = new PurchaseResult(
35+
PurchaseResultStatus.SUCCESS,
36+
null,
37+
null,
38+
false,
39+
PurchaseResultSource.API,
40+
null
41+
);
42+
3343
jest.mock('../Mapper', () => {
34-
const MockDeferredTransaction = require('../../dto/DeferredTransaction').default;
3544
return {
3645
__esModule: true,
3746
default: {
@@ -42,18 +51,7 @@ jest.mock('../Mapper', () => {
4251
}
4352
return map;
4453
}),
45-
convertDeferredTransaction: jest.fn((payload: Record<string, any>) => {
46-
if (!payload) return null;
47-
return new MockDeferredTransaction(
48-
payload.productId ?? '',
49-
payload.transactionId ?? null,
50-
payload.originalTransactionId ?? null,
51-
payload.type ?? 'Unknown',
52-
payload.value ?? 0,
53-
payload.currency ?? null
54-
);
55-
}),
56-
convertPurchaseResult: jest.fn(),
54+
convertPurchaseResult: jest.fn(() => mockPurchaseResult),
5755
},
5856
};
5957
});
@@ -75,13 +73,13 @@ function createConfig() {
7573
);
7674
}
7775

78-
const sampleTransaction = {
79-
productId: 'premium_product',
80-
transactionId: 'txn_123',
81-
originalTransactionId: 'txn_001',
82-
type: 'Subscription',
83-
value: 9.99,
84-
currency: 'USD',
76+
const samplePurchaseResult = {
77+
status: 'Success',
78+
entitlements: null,
79+
error: null,
80+
isFallbackGenerated: false,
81+
source: 'Api',
82+
storeTransaction: null,
8583
};
8684

8785
const entitlementsPayload: Record<string, QEntitlement> = {
@@ -107,21 +105,17 @@ describe('QonversionInternal - DeferredPurchasesListener (native event)', () =>
107105
expect(RNQonversion.onDeferredPurchaseCompleted).toHaveBeenCalled();
108106
});
109107

110-
it('fires listener with DeferredTransaction when native event received', () => {
108+
it('fires listener with PurchaseResult when native event received', () => {
111109
const instance = new QonversionInternal(createConfig());
112110
const listener: DeferredPurchasesListener = { onDeferredPurchaseCompleted: jest.fn() };
113111

114112
instance.setDeferredPurchasesListener(listener);
115-
fireEvent('onDeferredPurchaseCompleted', sampleTransaction);
113+
fireEvent('onDeferredPurchaseCompleted', samplePurchaseResult);
116114

117115
expect(listener.onDeferredPurchaseCompleted).toHaveBeenCalledTimes(1);
118116
const arg = (listener.onDeferredPurchaseCompleted as jest.Mock).mock.calls[0][0];
119-
expect(arg).toBeInstanceOf(DeferredTransaction);
120-
expect(arg.productId).toBe('premium_product');
121-
expect(arg.transactionId).toBe('txn_123');
122-
expect(arg.type).toBe('Subscription');
123-
expect(arg.value).toBe(9.99);
124-
expect(arg.currency).toBe('USD');
117+
expect(arg).toBeInstanceOf(PurchaseResult);
118+
expect(arg.isSuccess).toBe(true);
125119
});
126120

127121
it('does not fire listener when no listener is set', () => {
@@ -139,7 +133,7 @@ describe('QonversionInternal - DeferredPurchasesListener (native event)', () =>
139133
instance.setDeferredPurchasesListener(listener1);
140134
instance.setDeferredPurchasesListener(listener2);
141135

142-
fireEvent('onDeferredPurchaseCompleted', sampleTransaction);
136+
fireEvent('onDeferredPurchaseCompleted', samplePurchaseResult);
143137

144138
expect(listener1.onDeferredPurchaseCompleted).not.toHaveBeenCalled();
145139
expect(listener2.onDeferredPurchaseCompleted).toHaveBeenCalledTimes(1);
@@ -225,7 +219,7 @@ describe('QonversionInternal - both listeners coexist', () => {
225219
expect(newListener.onDeferredPurchaseCompleted).not.toHaveBeenCalled();
226220

227221
// Deferred purchase fires new listener only
228-
fireEvent('onDeferredPurchaseCompleted', sampleTransaction);
222+
fireEvent('onDeferredPurchaseCompleted', samplePurchaseResult);
229223
expect(oldListener.onEntitlementsUpdated).toHaveBeenCalledTimes(1); // still 1
230224
expect(newListener.onDeferredPurchaseCompleted).toHaveBeenCalledTimes(1);
231225
});

0 commit comments

Comments
 (0)