Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix / waiting for payment PPV/TVOD offers #541

Merged
merged 5 commits into from
Jun 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/common/src/controllers/AccountController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ export default class AccountController {
let pendingOffer: Offer | null = null;

if (!activeSubscription && !!retry && retry > 0) {
const retryDelay = 1500; // Any initial delay has already occured, so we can set this to a fixed value
const retryDelay = 1500; // Any initial delay has already occurred, so we can set this to a fixed value

return await this.reloadSubscriptions({ delay: retryDelay, retry: retry - 1 });
}
Expand Down
8 changes: 3 additions & 5 deletions packages/common/src/controllers/CheckoutController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,13 +242,11 @@ export default class CheckoutController {
const { getAccountInfo } = useAccountStore.getState();

const { customerId } = getAccountInfo();

assertModuleMethod(this.checkoutService.getSubscriptionSwitches, 'getSubscriptionSwitches is not available in checkout service');
assertModuleMethod(this.checkoutService.getOffer, 'getOffer is not available in checkout service');

const { subscription } = useAccountStore.getState();

if (!subscription) return null;
if (!subscription || !this.checkoutService.getSubscriptionSwitches) return null;

assertModuleMethod(this.checkoutService.getOffer, 'getOffer is not available in checkout service');

const response = await this.checkoutService.getSubscriptionSwitches({
customerId: customerId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,38 @@ export default class JWPCheckoutService extends CheckoutService {
};
};

private formatOffer = (offer: AccessFee): Offer => {
/**
* Format a (Cleeng like) offer id for the given access fee (pricing option). For JWP, we need the asset id and
* access fee id in some cases.
*/
private formatOfferId(offer: AccessFee) {
const ppvOffers = ['ppv', 'ppv_custom'];
const offerId = ppvOffers.includes(offer.access_type.name) ? `C${offer.id}` : `S${offer.id}`;

return ppvOffers.includes(offer.access_type.name) ? `C${offer.item_id}_${offer.id}` : `S${offer.item_id}_${offer.id}`;
}

/**
* Parse the given offer id and extract the asset id.
* The offer id might be the Cleeng format (`S<assetId>_<pricingOptionId>`) or the asset id as string.
*/
private parseOfferId(offerId: string | number) {
if (typeof offerId === 'string') {
// offer id format `S<assetId>_<pricingOptionId>`
if (offerId.startsWith('C') || offerId.startsWith('S')) {
return parseInt(offerId.slice(1).split('_')[0]);
}

// offer id format `<assetId>`
return parseInt(offerId);
}

return offerId;
}

private formatOffer = (offer: AccessFee): Offer => {
return {
id: offer.id,
offerId,
offerId: this.formatOfferId(offer),
offerCurrency: offer.currency,
customerPriceInclTax: offer.amount,
customerCurrency: offer.currency,
Expand Down Expand Up @@ -97,9 +122,9 @@ export default class JWPCheckoutService extends CheckoutService {

getOffers: GetOffers = async (payload) => {
const offers = await Promise.all(
payload.offerIds.map(async (assetId) => {
payload.offerIds.map(async (offerId) => {
try {
const { data } = await InPlayer.Asset.getAssetAccessFees(parseInt(`${assetId}`));
const { data } = await InPlayer.Asset.getAssetAccessFees(this.parseOfferId(offerId));

return data?.map((offer) => this.formatOffer(offer));
} catch {
Expand Down Expand Up @@ -217,7 +242,7 @@ export default class JWPCheckoutService extends CheckoutService {

getEntitlements: GetEntitlements = async ({ offerId }) => {
try {
const response = await InPlayer.Asset.checkAccessForAsset(parseInt(offerId));
const response = await InPlayer.Asset.checkAccessForAsset(this.parseOfferId(offerId));
return this.formatEntitlements(response.data.expires_at, true);
} catch {
return this.formatEntitlements();
Expand Down
18 changes: 11 additions & 7 deletions packages/hooks-react/src/useCheckAccess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ type IntervalCheckAccessPayload = {
interval?: number;
iterations?: number;
offerId?: string;
callback?: (hasAccess: boolean) => void;
callback?: ({ hasAccess, offerId }: { hasAccess: boolean; offerId: string }) => void;
};

const useCheckAccess = () => {
Expand All @@ -22,21 +22,25 @@ const useCheckAccess = () => {
const offers = checkoutController.getSubscriptionOfferIds();

const intervalCheckAccess = useCallback(
({ interval = 3000, iterations = 5, offerId, callback }: IntervalCheckAccessPayload) => {
if (!offerId && offers?.[0]) {
offerId = offers[0];
({ interval = 3000, iterations = 5, offerId = offers?.[0], callback }: IntervalCheckAccessPayload) => {
if (!offerId) {
callback?.({ hasAccess: false, offerId: '' });
return;
}

intervalRef.current = window.setInterval(async () => {
const hasAccess = await accountController.checkEntitlements(offerId);

if (hasAccess) {
await accountController.reloadSubscriptions({ retry: 10, delay: 2000 });
callback?.(true);
window.clearInterval(intervalRef.current);
// No duplicate retry mechanism. This can also be a TVOD offer which isn't validated using the
// reloadSubscriptions method.
await accountController.reloadSubscriptions();
callback?.({ hasAccess: true, offerId: offerId || '' });
} else if (--iterations === 0) {
window.clearInterval(intervalRef.current);
setErrorMessage(t('payment.longer_than_usual'));
callback?.(false);
callback?.({ hasAccess: false, offerId: offerId || '' });
}
}, interval);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,18 @@ const WaitingForPayment = () => {
interval: 3000,
iterations: 5,
offerId,
callback: (hasAccess) => {
callback: ({ hasAccess, offerId }) => {
if (!hasAccess) return;

announce(t('checkout.payment_success'), 'success');
navigate(modalURLFromLocation(location, 'welcome'));

// close the modal for PPV/TVOD offers
if (offerId.startsWith('C') || offerId.startsWith('P')) {
// @TODO should we show a dedicated modal for TVOD access?
navigate(modalURLFromLocation(location, null));
} else {
navigate(modalURLFromLocation(location, 'welcome'));
}
},
});
//eslint-disable-next-line
Expand Down
4 changes: 2 additions & 2 deletions platforms/web/test-e2e/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export default {
paymentFee: formatPrice(0, 'EUR', 'NL'),
},
inplayer: {
label: `label[for="S38279"]`,
label: `label[for="S118699_38279"]`,
price: formatPrice(6.99, 'EUR'),
paymentFee: formatPrice(0, 'EUR'),
},
Expand All @@ -93,7 +93,7 @@ export default {
paymentFee: formatPrice(0, 'EUR', 'NL'),
},
inplayer: {
label: `label[for="S38280"]`,
label: `label[for="S118699_38280"]`,
price: formatPrice(50, 'EUR'),
paymentFee: formatPrice(0, 'EUR'),
},
Expand Down
33 changes: 4 additions & 29 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1879,7 +1879,7 @@
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6"
integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==

"@jridgewell/set-array@^1.2.1":
"@jridgewell/set-array@^1.0.1", "@jridgewell/set-array@^1.2.1":
version "1.2.1"
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280"
integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==
Expand Down Expand Up @@ -9925,7 +9925,7 @@ [email protected]:
resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6"
integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==

"string-width-cjs@npm:string-width@^4.2.0":
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
Expand All @@ -9942,15 +9942,6 @@ string-width@^2.1.0:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^4.0.0"

string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"

string-width@^5.0.1, string-width@^5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
Expand Down Expand Up @@ -10048,7 +10039,7 @@ stringify-object@^3.3.0:
is-obj "^1.0.1"
is-regexp "^1.0.0"

"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
Expand All @@ -10069,13 +10060,6 @@ strip-ansi@^5.1.0:
dependencies:
ansi-regex "^4.1.0"

strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"

strip-ansi@^7.0.1, strip-ansi@^7.1.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
Expand Down Expand Up @@ -11689,16 +11673,7 @@ [email protected]:
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343"
integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==

"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"

wrap-ansi@^7.0.0:
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
Expand Down
Loading