diff --git a/.changeset/tasty-foxes-work.md b/.changeset/tasty-foxes-work.md new file mode 100644 index 000000000..cbd9b818b --- /dev/null +++ b/.changeset/tasty-foxes-work.md @@ -0,0 +1,5 @@ +--- +'@shopify/shopify-api': minor +--- + +Add new trialDaysOverride parameter to billing.request, so that apps can override the default config value. diff --git a/docs/reference/billing/request.md b/docs/reference/billing/request.md index d948bf033..b33101121 100644 --- a/docs/reference/billing/request.md +++ b/docs/reference/billing/request.md @@ -136,6 +136,12 @@ Which URL to redirect the merchant to after the charge is confirmed. Whether to return the `confirmationUrl` as a `string`, or to return a more detailed object (see below). +### trialDaysOverride + +`number` + +Override value for the `trialDays` config option. Only applies to recurring purchases. + ## Return ### if `returnObject` parameter is `false` (default) diff --git a/lib/billing/__tests__/request.test.ts b/lib/billing/__tests__/request.test.ts index 57481eafa..54a483aa8 100644 --- a/lib/billing/__tests__/request.test.ts +++ b/lib/billing/__tests__/request.test.ts @@ -407,4 +407,72 @@ describe('shopify.billing.request', () => { }); }); }); + + it('applies trialDays override when set', async () => { + shopify = shopifyApi({ + ...testConfig, + billing: { + [Responses.PLAN_1]: { + amount: 5, + currencyCode: 'USD', + interval: BillingInterval.Every30Days, + replacementBehavior: BillingReplacementBehavior.ApplyImmediately, + trialDays: 10, + }, + }, + }); + + queueMockResponses([Responses.PURCHASE_SUBSCRIPTION_RESPONSE]); + + await shopify.billing.request({ + session, + plan: Responses.PLAN_1, + returnObject: true, + trialDaysOverride: 20, + }); + + expect({ + ...GRAPHQL_BASE_REQUEST, + data: { + query: expect.stringContaining('appSubscriptionCreate'), + variables: expect.objectContaining({ + trialDays: 20, + }), + }, + }).toMatchMadeHttpRequest(); + }); + + it('applies a trialDays override of 0', async () => { + shopify = shopifyApi({ + ...testConfig, + billing: { + [Responses.PLAN_1]: { + amount: 5, + currencyCode: 'USD', + interval: BillingInterval.Every30Days, + replacementBehavior: BillingReplacementBehavior.ApplyImmediately, + trialDays: 10, + }, + }, + }); + + queueMockResponses([Responses.PURCHASE_SUBSCRIPTION_RESPONSE]); + + await shopify.billing.request({ + session, + plan: Responses.PLAN_1, + returnObject: true, + trialDaysOverride: 0, + }); + + expect({ + ...GRAPHQL_BASE_REQUEST, + data: { + query: expect.stringContaining('appSubscriptionCreate'), + variables: expect.objectContaining({ + trialDays: 0, + }), + }, + }).toMatchMadeHttpRequest(); + }); }); diff --git a/lib/billing/request.ts b/lib/billing/request.ts index df37cf7ff..fc48e5534 100644 --- a/lib/billing/request.ts +++ b/lib/billing/request.ts @@ -29,6 +29,7 @@ interface RequestInternalParams { interface RequestSubscriptionInternalParams extends RequestInternalParams { billingConfig: BillingConfigSubscriptionPlan; + trialDaysOverride?: number; } interface RequestOneTimePaymentInternalParams extends RequestInternalParams { @@ -37,6 +38,7 @@ interface RequestOneTimePaymentInternalParams extends RequestInternalParams { interface RequestUsageSubscriptionInternalParams extends RequestInternalParams { billingConfig: BillingConfigUsagePlan; + trialDaysOverride?: number; } export function request(config: ConfigInterface) { @@ -46,6 +48,7 @@ export function request(config: ConfigInterface) { isTest = true, returnUrl: returnUrlParam, returnObject = false, + trialDaysOverride = undefined, }: Params): Promise> { if (!config.billing || !config.billing[plan]) { throw new BillingError({ @@ -90,6 +93,7 @@ export function request(config: ConfigInterface) { client, returnUrl, isTest, + trialDaysOverride, }); data = mutationUsageResponse.data.appSubscriptionCreate; break; @@ -101,6 +105,7 @@ export function request(config: ConfigInterface) { client, returnUrl, isTest, + trialDaysOverride, }); data = mutationRecurringResponse.data.appSubscriptionCreate; } @@ -129,13 +134,18 @@ async function requestRecurringPayment({ client, returnUrl, isTest, + trialDaysOverride, }: RequestSubscriptionInternalParams): Promise { + const trialDays = + trialDaysOverride === undefined + ? billingConfig.trialDays + : trialDaysOverride; const mutationResponse = await client.query({ data: { query: RECURRING_PURCHASE_MUTATION, variables: { name: plan, - trialDays: billingConfig.trialDays, + trialDays, replacementBehavior: billingConfig.replacementBehavior, returnUrl, test: isTest, @@ -180,7 +190,12 @@ async function requestUsagePayment({ client, returnUrl, isTest, + trialDaysOverride, }: RequestUsageSubscriptionInternalParams): Promise { + const trialDays = + trialDaysOverride === undefined + ? billingConfig.trialDays + : trialDaysOverride; const mutationResponse = await client.query({ data: { query: RECURRING_PURCHASE_MUTATION, @@ -188,7 +203,7 @@ async function requestUsagePayment({ name: plan, returnUrl, test: isTest, - trialDays: billingConfig.trialDays, + trialDays, replacementBehavior: billingConfig.replacementBehavior, lineItems: [ { diff --git a/lib/billing/types.ts b/lib/billing/types.ts index 345bae574..f51ac974e 100644 --- a/lib/billing/types.ts +++ b/lib/billing/types.ts @@ -74,6 +74,7 @@ export interface BillingRequestParams { isTest?: boolean; returnUrl?: string; returnObject?: boolean; + trialDaysOverride?: number; } export interface BillingRequestResponseObject {