Skip to content

Commit deb8188

Browse files
Add workflowIdentifier to PresentedOfferingContext interface
1 parent e0d23ec commit deb8188

File tree

3 files changed

+118
-2
lines changed

3 files changed

+118
-2
lines changed

src/main.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,7 @@ export class Purchases {
408408
rcSource: this._flags.rcSource ?? null,
409409
workflowContext: this._context?.workflowContext,
410410
});
411-
this.backend = new Backend(this._API_KEY, httpConfig);
411+
this.backend = new Backend(this._API_KEY, httpConfig, this._context);
412412
this.inMemoryCache = new InMemoryCache();
413413
this.purchaseOperationHelper = new PurchaseOperationHelper(
414414
this.backend,

src/networking/backend.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import type {
2525
PurchaseMetadata,
2626
PurchaseOption,
2727
} from "../entities/offerings";
28+
import type { PurchasesContext } from "../entities/purchases-config";
2829
import type { CheckoutCompleteResponse } from "./responses/checkout-complete-response";
2930
import type { CheckoutCalculateTaxResponse } from "./responses/checkout-calculate-tax-response";
3031
import { SetAttributesEndpoint } from "./endpoints";
@@ -35,11 +36,17 @@ export class Backend {
3536
private readonly API_KEY: string;
3637
private readonly httpConfig: HttpConfig;
3738
private readonly isSandbox: boolean;
39+
private readonly purchasesContext?: PurchasesContext;
3840

39-
constructor(API_KEY: string, httpConfig: HttpConfig = defaultHttpConfig) {
41+
constructor(
42+
API_KEY: string,
43+
httpConfig: HttpConfig = defaultHttpConfig,
44+
purchasesContext?: PurchasesContext,
45+
) {
4046
this.API_KEY = API_KEY;
4147
this.httpConfig = httpConfig;
4248
this.isSandbox = isWebBillingSandboxApiKey(API_KEY);
49+
this.purchasesContext = purchasesContext;
4350
}
4451

4552
getIsSandbox(): boolean {
@@ -129,6 +136,7 @@ export class Backend {
129136
presented_offering_identifier: string;
130137
price_id: string;
131138
presented_placement_identifier?: string;
139+
presented_workflow_id?: string;
132140
offer_id?: string;
133141
applied_targeting_rule?: {
134142
rule_id: string;
@@ -169,6 +177,11 @@ export class Backend {
169177
presentedOfferingContext.placementIdentifier;
170178
}
171179

180+
if (this.purchasesContext?.workflowContext?.workflowIdentifier) {
181+
requestBody.presented_workflow_id =
182+
this.purchasesContext.workflowContext.workflowIdentifier;
183+
}
184+
172185
return await performRequest<
173186
CheckoutStartRequestBody,
174187
CheckoutStartResponse
@@ -299,6 +312,7 @@ export class Backend {
299312
app_user_id: string;
300313
presented_offering_identifier: string;
301314
presented_placement_identifier: string | null;
315+
presented_workflow_id?: string | null;
302316
applied_targeting_rule?: PostReceiptTargetingRule | null;
303317
initiation_source: string;
304318
};
@@ -320,6 +334,8 @@ export class Backend {
320334
presentedOfferingContext.offeringIdentifier,
321335
presented_placement_identifier:
322336
presentedOfferingContext.placementIdentifier,
337+
presented_workflow_id:
338+
this.purchasesContext?.workflowContext?.workflowIdentifier,
323339
applied_targeting_rule: targetingInfo,
324340
initiation_source: initiationSource,
325341
};

src/tests/networking/backend.test.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,56 @@ describe("postCheckoutStart request", () => {
578578
expect(result).toEqual(checkoutStartResponse);
579579
});
580580

581+
test("handles workflow identifier correctly", async () => {
582+
const backendWithContext = new Backend("test_api_key", undefined, {
583+
workflowContext: { workflowIdentifier: "workflow_456" },
584+
});
585+
586+
setCheckoutStartResponse(
587+
HttpResponse.json(checkoutStartResponse, { status: 200 }),
588+
);
589+
590+
await backendWithContext.postCheckoutStart(
591+
"someAppUserId",
592+
"monthly",
593+
{
594+
offeringIdentifier: "offering_1",
595+
targetingContext: null,
596+
placementIdentifier: null,
597+
},
598+
{ id: "base_option", priceId: "test_price_id" },
599+
"test-trace-id",
600+
);
601+
602+
expect(purchaseMethodAPIMock).toHaveBeenCalledTimes(1);
603+
const request = purchaseMethodAPIMock.mock.calls[0][0].request;
604+
const requestBody = await request.json();
605+
expect(requestBody.presented_workflow_id).toBe("workflow_456");
606+
});
607+
608+
test("omits workflow identifier from request when not present", async () => {
609+
setCheckoutStartResponse(
610+
HttpResponse.json(checkoutStartResponse, { status: 200 }),
611+
);
612+
613+
await backend.postCheckoutStart(
614+
"someAppUserId",
615+
"monthly",
616+
{
617+
offeringIdentifier: "offering_1",
618+
targetingContext: null,
619+
placementIdentifier: null,
620+
},
621+
{ id: "base_option", priceId: "test_price_id" },
622+
"test-trace-id",
623+
);
624+
625+
expect(purchaseMethodAPIMock).toHaveBeenCalledTimes(1);
626+
const request = purchaseMethodAPIMock.mock.calls[0][0].request;
627+
const requestBody = await request.json();
628+
expect(requestBody.presented_workflow_id).toBeUndefined();
629+
});
630+
581631
test("throws an error if the backend returns a server error", async () => {
582632
setCheckoutStartResponse(
583633
HttpResponse.json(null, { status: StatusCodes.INTERNAL_SERVER_ERROR }),
@@ -1067,6 +1117,56 @@ describe("postReceipt request", () => {
10671117
expect(requestBody.presented_placement_identifier).toBe("home_screen");
10681118
});
10691119

1120+
test("handles workflow identifier correctly", async () => {
1121+
const backendWithContext = new Backend("test_api_key", undefined, {
1122+
workflowContext: { workflowIdentifier: "workflow_123" },
1123+
});
1124+
1125+
setPostReceiptResponse(
1126+
HttpResponse.json(customerInfoResponse, { status: 200 }),
1127+
);
1128+
1129+
await backendWithContext.postReceipt(
1130+
"someAppUserId",
1131+
"monthly",
1132+
"EUR",
1133+
"test_fetch_token",
1134+
{
1135+
offeringIdentifier: "offering_1",
1136+
targetingContext: null,
1137+
placementIdentifier: null,
1138+
},
1139+
"purchase",
1140+
);
1141+
1142+
const request = postReceiptAPIMock.mock.calls[0][0].request;
1143+
const requestBody = await request.json();
1144+
expect(requestBody.presented_workflow_id).toBe("workflow_123");
1145+
});
1146+
1147+
test("omits workflow identifier from postReceipt when not present", async () => {
1148+
setPostReceiptResponse(
1149+
HttpResponse.json(customerInfoResponse, { status: 200 }),
1150+
);
1151+
1152+
await backend.postReceipt(
1153+
"someAppUserId",
1154+
"monthly",
1155+
"EUR",
1156+
"test_fetch_token",
1157+
{
1158+
offeringIdentifier: "offering_1",
1159+
targetingContext: null,
1160+
placementIdentifier: null,
1161+
},
1162+
"purchase",
1163+
);
1164+
1165+
const request = postReceiptAPIMock.mock.calls[0][0].request;
1166+
const requestBody = await request.json();
1167+
expect(requestBody.presented_workflow_id).toBeUndefined();
1168+
});
1169+
10701170
test("throws an error if the backend returns a server error", async () => {
10711171
setPostReceiptResponse(
10721172
HttpResponse.json(null, { status: StatusCodes.INTERNAL_SERVER_ERROR }),

0 commit comments

Comments
 (0)