@agencycoda/ngx-paypal
Version:
Paypal integration for Angular
356 lines (348 loc) • 12.9 kB
JavaScript
import { CommonModule } from '@angular/common';
import { Injectable, NgZone, EventEmitter, Component, ChangeDetectionStrategy, ChangeDetectorRef, Input, Output, ViewChild, NgModule } from '@angular/core';
import { Subject } from 'rxjs';
class ScriptService {
constructor(zone) {
this.zone = zone;
}
registerScript(url, globalVar, onReady) {
const existingGlobalVar = window[globalVar];
if (existingGlobalVar) {
// global variable is present = script was already loaded
this.zone.run(() => {
onReady(existingGlobalVar);
});
return;
}
// prepare script elem
const scriptElem = document.createElement('script');
scriptElem.id = this.getElemId(globalVar);
scriptElem.innerHTML = '';
scriptElem.onload = () => {
this.zone.run(() => {
onReady(window[globalVar]);
});
};
scriptElem.src = url;
scriptElem.async = true;
scriptElem.defer = true;
// add script to header
document.getElementsByTagName('head')[0].appendChild(scriptElem);
}
cleanup(globalVar) {
// remove script from DOM
const scriptElem = document.getElementById(this.getElemId(globalVar));
if (scriptElem) {
scriptElem.remove();
}
}
getElemId(globalVar) {
return `ngx-paypal-script-elem-${globalVar}`;
}
}
ScriptService.decorators = [
{ type: Injectable }
];
/** @nocollapse */
ScriptService.ctorParameters = () => [
{ type: NgZone }
];
class PayPalScriptService {
constructor(scriptService) {
this.scriptService = scriptService;
this.paypalWindowName = 'paypal';
}
registerPayPalScript(config, onReady) {
this.scriptService.registerScript(this.getUrlForConfig(config), this.paypalWindowName, onReady);
}
destroyPayPalScript() {
this.scriptService.cleanup(this.paypalWindowName);
}
getUrlForConfig(config) {
const params = [
{
name: 'client-id',
value: config.clientId
}
];
if (config.currency) {
params.push({
name: 'currency',
value: config.currency
});
}
if (config.commit) {
params.push({
name: 'commit',
value: config.commit
});
}
if (config.vault) {
params.push({
name: 'vault',
value: config.vault
});
}
if (config.extraParams) {
params.push(...config.extraParams);
}
return `https://www.paypal.com/sdk/js${this.getQueryString(params)}`;
}
getQueryString(queryParams) {
let queryString = '';
for (let i = 0; i < queryParams.length; i++) {
const queryParam = queryParams[i];
if (i === 0) {
queryString += '?';
}
else {
queryString += '&';
}
queryString += `${queryParam.name}=${queryParam.value}`;
}
return queryString;
}
}
PayPalScriptService.decorators = [
{ type: Injectable }
];
/** @nocollapse */
PayPalScriptService.ctorParameters = () => [
{ type: ScriptService }
];
class NgxPaypalComponent {
constructor(paypalScriptService, cdr, ngZone) {
this.paypalScriptService = paypalScriptService;
this.cdr = cdr;
this.ngZone = ngZone;
/**
* If enabled, paypal SDK script will be loaded. Useful if you want to have multiple PayPal components on the same page
* sharing base configuration. In such a case only a single component may register script.
*/
this.registerScript = true;
/**
* Emitted when paypal script is loaded
*/
this.scriptLoaded = new EventEmitter();
this.ngUnsubscribe = new Subject();
/**
* Flag that indicates if paypal should be initialized (required for handling script load events and availability of DOM element)
*/
this.initializePayPal = true;
}
set payPalButtonContainer(content) {
this.payPalButtonContainerElem = content;
}
ngOnChanges(changes) {
if (!this.payPalButtonContainerId) {
this.payPalButtonContainerId = this.generateElementId();
}
// first time config setup
const config = this.config;
if (changes.config.isFirstChange()) {
if (config && this.registerScript) {
this.initPayPalScript(config, (payPal) => {
// store reference to paypal global script
this.payPal = payPal;
this.doPayPalCheck();
});
}
}
// changes to config
if (!changes.config.isFirstChange()) {
this.reinitialize(config);
}
}
ngOnDestroy() {
this.paypalScriptService.destroyPayPalScript();
this.ngUnsubscribe.next();
this.ngUnsubscribe.complete();
}
ngAfterViewInit() {
this.doPayPalCheck();
}
customInit(payPal) {
this.payPal = payPal;
this.doPayPalCheck();
}
reinitialize(config) {
this.config = config;
this.payPal = undefined;
this.paypalScriptService.destroyPayPalScript();
this.payPalButtonContainerId = this.generateElementId();
this.initializePayPal = true;
if (this.payPalButtonContainerElem) {
while (this.payPalButtonContainerElem.nativeElement.firstChild) {
this.payPalButtonContainerElem.nativeElement.removeChild(this.payPalButtonContainerElem.nativeElement.firstChild);
}
}
this.cdr.detectChanges();
if (this.config) {
if (!this.payPal) {
this.initPayPalScript(this.config, (payPal) => {
// store reference to paypal global script
this.payPal = payPal;
this.doPayPalCheck();
});
}
else {
this.doPayPalCheck();
}
}
}
doPayPalCheck() {
if (this.initializePayPal && this.config && this.payPal && this.payPalButtonContainerElem) {
// make sure that id is also set
if (this.payPalButtonContainerElem.nativeElement.id) {
this.initializePayPal = false;
this.initPayPal(this.config, this.payPal);
}
}
}
initPayPalScript(config, initPayPal) {
this.paypalScriptService.registerPayPalScript({
clientId: config.clientId,
commit: config.advanced && config.advanced.commit ? config.advanced.commit : undefined,
currency: config.currency,
vault: config.vault,
extraParams: config.advanced && config.advanced.extraQueryParams ? config.advanced.extraQueryParams : []
}, (paypal) => {
this.scriptLoaded.next(paypal);
initPayPal(paypal);
});
}
generateElementId() {
return `ngx-captcha-id-${new Date().valueOf()}`;
}
initPayPal(config, paypal) {
// Running outside angular zone prevents infinite ngDoCheck lifecycle calls
this.ngZone.runOutsideAngular(() => {
// https://developer.paypal.com/docs/checkout/integrate/#2-add-the-paypal-script-to-your-web-page
const createOrder = (data, actions) => {
return this.ngZone.run(() => {
if (config.createOrderOnClient && config.createOrderOnServer) {
throw Error(`Both 'createOrderOnClient' and 'createOrderOnServer' are defined.
Please choose one or the other.`);
}
if (!config.createOrderOnClient && !config.createOrderOnServer) {
throw Error(`Neither 'createOrderOnClient' or 'createOrderOnServer' are defined.
Please define one of these to create order.`);
}
if (config.createOrderOnClient) {
return actions.order.create(config.createOrderOnClient(data));
}
if (config.createOrderOnServer) {
return config.createOrderOnServer(data);
}
throw Error(`Invalid state for 'createOrder'.`);
});
};
const createSubscription = (data, actions) => {
return this.ngZone.run(() => {
if (config.createSubscription) {
return config.createSubscription(data, actions);
}
});
};
const onShippingChange = (data, actions) => {
return this.ngZone.run(() => {
if (config.onShippingChange) {
return config.onShippingChange(data, actions);
}
});
};
const buttonsConfig = Object.assign(Object.assign(Object.assign({ style: config.style, onApprove: (data, actions) => {
return this.ngZone.run(() => {
if (config.onApprove) {
config.onApprove(data, actions);
}
// capture on server
if (config.authorizeOnServer) {
return config.authorizeOnServer(data, actions);
}
// capture on client
const onClientAuthorization = config.onClientAuthorization;
if (onClientAuthorization) {
actions.order.capture().then((details) => {
this.ngZone.run(() => {
onClientAuthorization(details);
});
});
return;
}
});
}, onError: (error) => {
this.ngZone.run(() => {
if (config.onError) {
config.onError(error);
}
});
}, onCancel: (data, actions) => {
this.ngZone.run(() => {
if (config.onCancel) {
config.onCancel(data, actions);
}
});
}, onClick: (data, actions) => {
this.ngZone.run(() => {
if (config.onClick) {
config.onClick(data, actions);
}
});
}, onInit: (data, actions) => {
this.ngZone.run(() => {
if (config.onInit) {
config.onInit(data, actions);
}
});
} }, ((config.createOrderOnClient || config.createOrderOnServer) && { createOrder })), (config.createSubscription && { createSubscription })), (config.onShippingChange && { onShippingChange }));
paypal.Buttons(buttonsConfig).render(`#${this.payPalButtonContainerId}`);
});
}
}
NgxPaypalComponent.decorators = [
{ type: Component, args: [{
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'ngx-paypal',
template: `
<div #payPalButtonContainer [id]="payPalButtonContainerId"></div>
`
},] }
];
/** @nocollapse */
NgxPaypalComponent.ctorParameters = () => [
{ type: PayPalScriptService },
{ type: ChangeDetectorRef },
{ type: NgZone }
];
NgxPaypalComponent.propDecorators = {
config: [{ type: Input }],
registerScript: [{ type: Input }],
scriptLoaded: [{ type: Output }],
payPalButtonContainer: [{ type: ViewChild, args: ['payPalButtonContainer', { static: false },] }]
};
class NgxPayPalModule {
}
NgxPayPalModule.decorators = [
{ type: NgModule, args: [{
imports: [
CommonModule
],
declarations: [
NgxPaypalComponent,
],
exports: [
NgxPaypalComponent,
],
providers: [
ScriptService,
PayPalScriptService
]
},] }
];
/** Public API */
/**
* Generated bundle index. Do not edit.
*/
export { NgxPayPalModule, NgxPaypalComponent, PayPalScriptService, ScriptService as ɵa };
//# sourceMappingURL=agencycoda-ngx-paypal.js.map