/* Copyright 2024 Schibsted Products & Technology AS. Licensed under the terms of the MIT license.
* See LICENSE.md in the project root.
*/
'use strict';
import { assert, isNonEmptyString, isUrl, isStr } from './validate.js';
import { urlMapper } from './url.js';
import { ENDPOINTS } from './config.js';
import * as popup from './popup.js';
import RESTClient from './RESTClient.js';
import * as spidTalk from './spidTalk.js';
const globalWindow = () => window;
/**
* Provides features related to payment
*/
export class Payment {
/**
* @param {object} options
* @param {string} options.clientId - Mandatory client id
* @param {string} [options.redirectUri] - Redirect uri
* @param {string} [options.env=PRE] - Schibsted account environment: `PRE`, `PRO` or `PRO_NO`
* @param {string} [options.publisher] - ZUORA publisher
* @param {object} [options.window]
*
* @throws {SDKError} - If any of options are invalid
*/
constructor({ clientId, redirectUri, env = 'PRE', publisher, window = globalWindow() }) {
spidTalk.emulate(window);
assert(isNonEmptyString(clientId), 'clientId parameter is required');
this.clientId = clientId;
this.redirectUri = redirectUri;
this.window = window;
this.publisher = publisher;
this._setSpidServerUrl(env);
this._setBffServerUrl(env);
}
/**
* Set SPiD server URL
* @private
* @param {string} url - real URL or 'PRE' style key
* @returns {void}
*/
_setSpidServerUrl(url) {
assert(isStr(url), `url parameter is invalid: ${url}`);
this._spid = new RESTClient({
serverUrl: urlMapper(url, ENDPOINTS.SPiD),
defaultParams: { client_id: this.clientId, redirect_uri: this.redirectUri },
});
}
/**
* Set BFF server URL - real URL or 'PRE' style key
* @private
* @param {string} url
* @returns {void}
*/
_setBffServerUrl(url) {
assert(isStr(url), `url parameter is invalid: ${url}`);
this._bff = new RESTClient({
serverUrl: urlMapper(url, ENDPOINTS.BFF),
defaultParams: { client_id: this.clientId, redirect_uri: this.redirectUri },
});
}
/**
* Close this.popup if it exists and is open
* @private
* @returns {void}
*/
_closePopup() {
if (this.popup) {
if (!this.popup.closed) {
this.popup.close();
}
this.popup = null;
}
}
/**
* Starts the flow for the paylink in a popup or current window
* @param {object} options
* @param {string} options.paylink - The paylink
* @param {boolean} [options.preferPopup=false] - Should we try to open a popup?
* @param {string} [options.redirectUri=this.redirectUri]
* @returns {Window|null} - Returns a reference to the popup window, or `null` if no popup was
* used
*/
payWithPaylink({ paylink, preferPopup, redirectUri = this.redirectUri }) {
assert(isUrl(redirectUri), `payWithPaylink(): redirectUri is invalid`);
this._closePopup();
const url = this.purchasePaylinkUrl(paylink, redirectUri);
if (preferPopup) {
this.popup =
popup.open(this.window, url, 'Schibsted account', { width: 360, height: 570 });
if (this.popup) {
return this.popup;
}
}
this.window.location.href = url;
return null;
}
/**
* Get the url for the end user to review the purchase history
* @param {string} [redirectUri=this.redirectUri]
* @return {string} - The url to the purchase history review page
*/
purchaseHistoryUrl(redirectUri = this.redirectUri) {
assert(isUrl(redirectUri), `purchaseHistoryUrl(): redirectUri is invalid`);
return this._spid.makeUrl('account/purchasehistory', { redirect_uri: redirectUri });
}
/**
* Get the url for the end user to redeem a voucher code
* @param {string} voucherCode
* @param {string} [redirectUri=this.redirectUri]
* @return {string} - The url
*/
redeemUrl(voucherCode, redirectUri = this.redirectUri) {
assert(isUrl(redirectUri), `redeemUrl(): redirectUri is invalid`);
return this._spid.makeUrl('account/redeem', { voucher_code: voucherCode });
}
/**
* @deprecated https://github.com/schibsted/account-sdk-browser/issues/94
*
* Get the url for the paylink purchase
* @todo Check working-ness for BFF + SPiD
* @param {string} paylinkId
* @param {string} [redirectUri=this.redirectUri]
* @return {string} - The url to the API endpoint
*/
purchasePaylinkUrl(paylinkId, redirectUri = this.redirectUri) {
assert(isUrl(redirectUri), `purchasePaylinkUrl(): redirectUri is invalid`);
assert(isNonEmptyString(paylinkId), `purchasePaylinkUrl(): paylinkId is required`);
return this._bff.makeUrl('payment/purchase', {
paylink: paylinkId,
redirect_uri: redirectUri
});
}
/**
* Get the url for flow to purchase a product
* @param {string} productId
* @param {string} [redirectUri=this.redirectUri]
* @return {string} - The url to the products review page
*/
purchaseProductFlowUrl(productId, redirectUri = this.redirectUri) {
assert(isUrl(redirectUri), `purchaseProductUrl(): redirectUri is invalid`);
assert(isNonEmptyString(productId), `purchaseProductFlowUrl(): productId is required`);
return this._bff.makeUrl('flow/checkout', {
response_type: 'code',
flow: 'payment',
product_id: productId,
redirect_uri: redirectUri
});
}
/**
* Get the url for flow to purchase a product through a campaign and voucher code
* @todo Check working-ness for BFF + SPiD
* @param {string} campaignId
* @param {string} productId
* @param {string} [voucherCode]
* @param {string} [redirectUri=this.redirectUri]
* @return {string} - The url to the products review page
*/
purchaseCampaignFlowUrl(campaignId, productId, voucherCode, redirectUri = this.redirectUri) {
assert(isUrl(redirectUri), `purchaseCampaignFlowUrl(): redirectUri is invalid`);
assert(isNonEmptyString(campaignId), `purchaseCampaignFlowUrl(): campaignId is required`);
assert(isNonEmptyString(productId), `purchaseCampaignFlowUrl(): productId is required`);
return this._bff.makeUrl('flow/checkout', {
response_type: 'code',
flow: 'payment',
campaign_id: campaignId,
product_id: productId,
voucher_code: voucherCode,
redirect_uri: redirectUri
});
}
/**
* @deprecated
* Get the url for flow to purchase a promo code product with ZUORA
* @param {string} code - promocode product code
* @param {string} [state=''] - An opaque value used by the client to maintain state between
* the request and callback. It's also recommended to prevent CSRF
* @param {string} [redirectUri=this.redirectUri]
* @return {string} - The url to the buy promo code product flow
*/
purchasePromoCodeProductFlowUrl(code, state = '', redirectUri = this.redirectUri) {
assert(isUrl(redirectUri), `purchasePromoCodeProductFlowUrl(): redirectUri is invalid`);
assert(isNonEmptyString(code), `purchasePromoCodeProductFlowUrl(): code is required`);
assert(isNonEmptyString(this.publisher), `purchasePromoCodeProductFlowUrl(): publisher is required in the constructor`);
return this._bff.makeUrl('payment/purchase/code', {
code,
publisher: this.publisher,
state,
redirect_uri: redirectUri
});
}
}
export default Payment;