paytm-blink-checkout-vue
Version:
Vue js based implementation for Paytm Blink Checkout JS
306 lines (286 loc) • 9.09 kB
JavaScript
var PaytmBlinkCheckoutVue=(function(exports,vueDemi){'use strict';const CONSTANTS = {
PROJECT_NAME: 'Paytm Blink Checkout JS',
ENV: {
PROD: 'PROD',
STAGE: 'STAGE'
},
HOSTS: {
PROD: 'https://securegw.paytm.in',
STAGE: 'https://securegw-stage.paytm.in'
},
LINKS: {
CHECKOUT_JS_URL: '/merchantpgpui/checkoutjs/merchants/'
},
ERRORS: {
INIT : 'An error during initialization!',
INVOKE : 'An error during invoking!',
MERCHANT_ID_NOT_FOUND: 'Please provide merchant id!',
CHECKOUT_NOT_AVAILABLE: 'Checkout JS library not found. Please make sure you have included checkout js!',
INVALID_CHECKOUT_JS_INSTANCE: 'Invalid instance provided!'
},
IDS: {
CHECKOUT_ELEMENT: 'checkout-wrapper-'
}
};
// Prefix error with project name
Object.keys(CONSTANTS.ERRORS).forEach(errorCode=>{
CONSTANTS.ERRORS[errorCode] = `${CONSTANTS.PROJECT_NAME}: ${CONSTANTS.ERRORS[errorCode]}`;
});function isTrue(value) {
return typeof value === "boolean"
? value
: typeof value === "string"
? value === "true"
: false;
}
function propChangedHandler(newVal, oldVal) {
oldVal !== newVal && this.preSetup();
}
/**
* The component is responsible for setting up Checkout JS library.
* It sets the Checkout JS instance and make it available to all its children
* component via Provider. It requires config property which is
* mandatory is order to initialize Checkout JS library. Additionally openInPopup
* prop can also be passed to show checkout in popup or not, by default its
* value is true.
* The config should be of same format as the Checkout JS library, which
* could be checked from this
* [link](https://developer.paytm.com/docs/js-checkout/configuration/options/?ref=jsCheckout).
*
* Example
* <CheckoutProvider :config="config" env="PROD" :openInPopup="openInPopup">
* </CheckoutProvider>
*/
var CheckoutProvider = vueDemi.defineComponent({
name: "CheckoutProvider",
data() {
return {
isScriptLoaded: false,
isScriptLoading: false,
localConfig: null,
};
},
setup() {
const localCheckoutJsInstance = vueDemi.ref(null);
const elementId = vueDemi.ref(
CONSTANTS.IDS.CHECKOUT_ELEMENT + new Date().getTime()
);
vueDemi.provide("checkoutJsInstance", localCheckoutJsInstance);
vueDemi.provide("elementId", elementId);
return {
localCheckoutJsInstance,
elementId,
};
},
props: {
config: Object,
checkoutJsInstance: Object,
openInPopup: Boolean,
env: String,
},
watch: {
config: propChangedHandler,
checkoutJsInstance: propChangedHandler,
openInPopup: propChangedHandler,
},
mounted() {
this.preSetup();
},
methods: {
preSetup() {
const { config, checkoutJsInstance } = this;
const merchantId = config && config.merchant && config.merchant.mid;
if (!merchantId) {
console.error(CONSTANTS.ERRORS.MERCHANT_ID_NOT_FOUND);
return;
}
const prevMerchantId =
this.localConfig &&
this.localConfig.merchant &&
this.localConfig.merchant.mid;
this.localConfig = config;
if (
(checkoutJsInstance || this.isScriptLoaded) &&
prevMerchantId === merchantId
) {
this.initializeCheckout();
} else if (
!this.isScriptLoading ||
(prevMerchantId && merchantId !== prevMerchantId)
) {
this.loadCheckoutScript(merchantId);
}
},
loadCheckoutScript(merchantId) {
const env =
CONSTANTS.ENV[(this.env || "").toUpperCase()] || CONSTANTS.ENV.PROD;
const scriptElement = document.createElement("script");
scriptElement.async = true;
scriptElement.src =
CONSTANTS.HOSTS[env] +
CONSTANTS.LINKS.CHECKOUT_JS_URL.concat(merchantId);
scriptElement.type = "application/javascript";
scriptElement.onload = this.setupCheckoutJsOnScriptLoad;
scriptElement.onerror = (error) => {
console.error(CONSTANTS.ERRORS.FAILED_TO_LOAD_SCRIPT, error);
this.isScriptLoading = false;
};
document.body.appendChild(scriptElement);
this.isScriptLoading = true;
},
setupCheckoutJsOnScriptLoad() {
const checkoutJsInstance = this.getCheckoutJsObj();
this.isScriptLoading = true;
this.isScriptLoaded = false;
if (checkoutJsInstance && checkoutJsInstance.onLoad) {
checkoutJsInstance.onLoad(() => {
this.isScriptLoading = false;
this.isScriptLoaded = true;
this.initializeCheckout();
});
} else {
console.error(CONSTANTS.ERRORS.INVALID_CHECKOUT_JS_INSTANCE);
}
},
initializeCheckout() {
const { openInPopup = true } = this;
// Set checkoutJsInstance via shallow copy so that invoke method in
// checkout component can be invoked via shallow comparison.
const checkoutJsInstance = { ...this.getCheckoutJsObj() };
if (
checkoutJsInstance &&
checkoutJsInstance.init &&
checkoutJsInstance.invoke
) {
checkoutJsInstance
.init({
...this.config,
root: isTrue(openInPopup) ? "" : `#${this.elementId}`,
})
.then(() => {
this.localCheckoutJsInstance = checkoutJsInstance;
})
.catch((error) => {
console.error(CONSTANTS.ERRORS.INIT, error);
});
} else {
console.error(CONSTANTS.ERRORS.INVALID_CHECKOUT_JS_INSTANCE);
}
},
getCheckoutJsObj() {
if (this.checkoutJsInstance) {
return this.checkoutJsInstance;
}
if (window && window.Paytm && window.Paytm.CheckoutJS) {
return window.Paytm.CheckoutJS;
}
console.warn(CONSTANTS.ERRORS.CHECKOUT_NOT_AVAILABLE);
return null;
},
},
render() {
return vueDemi.h(
"span",
{},
typeof this.$slots.default === 'function' ? this.$slots.default() : this.$slots.default
);
}
});var Checkout = vueDemi.defineComponent({
name: "Checkout",
setup() {
const elementId = vueDemi.inject("elementId");
const checkoutJsInstance = vueDemi.inject("checkoutJsInstance");
return {
elementId,
checkoutJsInstance,
};
},
watch: {
checkoutJsInstance: function (newVal, oldVal) {
oldVal !== newVal && this.invoke(newVal);
},
},
mounted() {
this.checkoutJsInstance && this.invoke(this.checkoutJsInstance);
},
methods: {
invoke(checkoutJsInstance) {
if (checkoutJsInstance && checkoutJsInstance.invoke) {
try {
checkoutJsInstance.invoke();
} catch (error) {
console.error(CONSTANTS.ERRORS.INVOKE, error);
}
}
},
},
render() {
return vueDemi.h(
"div",
vueDemi.isVue2
? {
attrs: {
id: this.elementId,
},
}
: {
id: this.elementId,
},
[]
);
},
});/**
* The higher order component injects Checkout JS instance to the wrapped component
* and make it available in the wrapped component props as checkoutJsInstance .
* The instance allows to directly interact with CheckoutJs library .
* The injected component should be always nested inside CheckoutProvider component.
*
* Example
*
* Component that makes use of checkoutJsInstance prop.
* export default {
* name: "Test",
* props: {
* checkoutJsInstance: Object
* }
* };
*
* Wrap component in a higher order component which provides checkoutJsInstance prop.
* const InjectedCheckout = injectCheckout(Test);
*
* Render the wrapped component
* <CheckoutProvider :config="config">
* <InjectedComponent />
* </CheckoutProvider>
*
*/
function InjectCheckout (WrappedComponent) {
const originalProps = WrappedComponent.props || {};
return {
setup() {
const checkoutJsInstance = vueDemi.inject("checkoutJsInstance");
return {
checkoutJsInstance,
};
},
render() {
return vueDemi.h(
WrappedComponent,
vueDemi.isVue2
? {
props: {
...this.$props,
checkoutJsInstance: this.checkoutJsInstance,
},
}
: {
...this.$props,
checkoutJsInstance: this.checkoutJsInstance,
},
typeof this.$slots.default === "function"
? this.$slots.default()
: this.$slots.default
);
},
props: [...Object.keys(originalProps).filter(prop => prop !== "checkoutJsInstance")],
};
}exports.Checkout=Checkout;exports.CheckoutProvider=CheckoutProvider;exports.injectCheckout=InjectCheckout;Object.defineProperty(exports,'__esModule',{value:true});return exports;}({},VueDemi));