UNPKG

paypal-express-checkout-es6

Version:
335 lines (297 loc) 10.6 kB
/* @flow */ import request from 'request'; import QS from 'querystring'; import debugLib from 'debug'; export type PaypalECConfigType = { isSandbox: bool, // true = sandbox, false = prod CURRENCYCODE: string, // USD USER: string, // api user name PWD: string, // api password SIGNATURE: string, // api signature VERSION: string // version of api }; // These are required parameters in every call export type PaypalECRequestParamType = { USER: string, PWD: string, SIGNATURE: string, METHOD: string, VERSION: string }; export type PaypalECResultType = { errorCode: ?string, statusCode: number, responseBody: string, result: ?Object }; export type PaypalECPaymentItemType = { NAME: string, // item name QTY: number, // total quantity of the items AMT: number, // price of an item TAXAMT: ?number, // tax amount of an item DESC: ?string, // item description NUMBER: ?string // item number generated by you }; export type PaypalECPaymentType = { AMT: number, // total $ amount of the payment ITEMAMT: ?number, // total amount of the item TAXAMT: ?number, // total amount of tax SHIPPINGAMT: ?number, // total amount of shipping HANDLINGAMT: ?number, // total amount of shiping handling SHIPDISCAMT: ?number, // total amount of shipping discount INSURANCEAMT: ?number, // total amount of insurance PAYMENTACTION: string, // payment action - Sale, Order, Authorization .... SELLERPAYPALACCOUNTID: ?string, // paypal email of seller PAYMENTREQUESTID: ?string, // unique id generated by you for tracking payment DESC: ?string, // generic description for the payment items: ?Array<PaypalECPaymentItemType> // items in detail }; // default config const defaultConfig: PaypalECConfigType = { isSandbox: true, CURRENCYCODE: 'USD', USER: '', PWD: '', SIGNATURE: '', VERSION: '86' }; // flow types for request type RequestErrorType = { code: string }; type RequestHttpResponseType = { statusCode: number }; /** * @class PaypalExpressCheckout */ export default class { /** * @property config */ config: PaypalECConfigType /** * @property _log */ _log: Function // used for logging /** * @method constructor * @param { Object } config - initial config */ constructor(config: Object) { this._log = debugLib('paypal-express-checkout'); this.setConfig(Object.assign({}, defaultConfig, config)); } /** * @method setConfig * @param { Object } config - new config to set */ setConfig(config: Object) { this.config = Object.assign({}, this.config, config); this._log('%s mode set', this.config.isSandbox ? 'Sandbox' : 'Production'); } /** * @method _getAPIEndpoint * @private * @returns {string} paypal API endpoint from config */ _getAPIEndpoint(): string { return this.config.isSandbox ? 'https://api-3t.sandbox.paypal.com/nvp' : 'https://api-3t.paypal.com/nvp'; } /** * @method _getRequestParams * @private * @param {string} method - api method * @param {Object} params - key,value based parameter list for api * @returns {PaypalECRequestParamType} parameter object for paypal express checkout call */ _getRequestParams(method: string, params: Object): PaypalECRequestParamType { return { USER: this.config.USER, PWD: this.config.PWD, VERSION: this.config.VERSION, SIGNATURE: this.config.SIGNATURE, METHOD: method, ...params }; } /** * @method call * @param {string} method - api method * @param {Object} params - key,value based parameter list for api * @returns {Promise<PaypalECResultType>} promise of api call result */ call(method: string, params: Object): Promise<PaypalECResultType> { const uri: string = this._getAPIEndpoint(); const form: PaypalECRequestParamType = this._getRequestParams(method, params); return new Promise((resolve: Function, reject: Function) => { this._log(`POST ${uri}`); this._log(`Form ${JSON.stringify(form, null, 2)}`); request.post({ uri, form }, (err: ?RequestErrorType, httpResp: RequestHttpResponseType, responseBody: string) => { const result: PaypalECResultType = { errorCode: null, responseBody, statusCode: httpResp.statusCode, result: QS.parse(responseBody) }; this._log(`Response http status ${result.statusCode}`); this._log(`Response error code ${result.errorCode}`); this._log(`Response result ${JSON.stringify(result.result, null, 2)}`); if (err) { result.errorCode = err.code; reject(result); } else { resolve(result); } }); }); } /** * @method getUrlFromToken * @param {string} token - auth token created from other funcs * @returns {string} Full paypal url to redirect user for actions */ getUrlFromToken(token: string): string { const sandboxText: string = this.config.isSandbox ? '.sandbox' : ''; return `https://www${sandboxText}.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=${token}`; } // ============================================== // Functions for parallel payment // ============================================== /** * @method _getPaymentRequestForm * @param {Array<PaypalECPaymentType>} paymentData - multiple payment info for parallel payment * @returns {Object} payment form data */ _getPaymentRequestForm(paymentData: Array<PaypalECPaymentType> = []): Object { const formData = {}; this._log(`${paymentData.length} parallel payments to do`); paymentData.forEach((payment: PaypalECPaymentType, index: number) => { formData[`PAYMENTREQUEST_${index}_CURRENCYCODE`] = this.config.CURRENCYCODE; Object.keys(payment).forEach((key: string) => { if (key !== 'items') { formData[`PAYMENTREQUEST_${index}_${key}`] = payment[key]; } }); if (payment.items) { this._log(`${payment.items.length} items presents in payment no.${index}`); payment.items.forEach((item: PaypalECPaymentItemType, itemIndex: number) => { Object.keys(item).forEach((key: string) => { formData[`L_PAYMENTREQUEST_${index}_${key}${itemIndex}`] = item[key]; }); }); } }); return formData; } /** * @method getExpressCheckoutDetails * @param {string} token - auth token * @returns {Promise<PaypalECResultType>} promise of api call result */ getExpressCheckoutDetails(token: string): Promise<PaypalECResultType> { const params: Object = { TOKEN: token }; return this.call('GetExpressCheckoutDetails', params); } /** * @method setExpressCheckout * @param {Array<PaypalECPaymentType>} paymentData - multiple payment info for parallel payment * @param {string} returnUrl - success callback url * @param {string} cancelUrl - cancel callback url * @param {string} additionalParams - additional parameters * @returns {Promise<PaypalECResultType>} promise of api call result */ setExpressCheckout(paymentData: Array<PaypalECPaymentType>, returnUrl: string, cancelUrl: string, additionalParams: Object = {}) : Promise<PaypalECResultType> { const params: Object = Object.assign({ VERSION: '93', cancelUrl, returnUrl, ...additionalParams }, this._getPaymentRequestForm(paymentData)); return this.call('SetExpressCheckout', params); } /** * @method doExpressCheckoutPayment * @param {string} token - auth token * @param {string} payerID - payerId returned from getExpressCheckoutDetails * @param {Array<PaypalECPaymentType>} paymentData - multiple payment info for parallel payment * @returns {Promise<PaypalECResultType>} promise of api call result */ doExpressCheckoutPayment(token: string, payerID: string, paymentData: Array<PaypalECPaymentType>) : Promise<PaypalECResultType> { // we only use necessary information from payment data const tunedPaymentData = paymentData.map((payment: PaypalECPaymentType): PaypalECPaymentType => { const { AMT, PAYMENTREQUESTID, SELLERPAYPALACCOUNTID } = payment; const data: PaypalECPaymentType = { AMT, PAYMENTREQUESTID, SELLERPAYPALACCOUNTID }; return data; }); const params: Object = Object.assign({ VERSION: '93', TOKEN: token, PAYERID: payerID }, this._getPaymentRequestForm(tunedPaymentData)); return this.call('DoExpressCheckoutPayment', params); } // ============================================== // Functions for reference payment // ============================================== /** * @method getBillingAuthorizationToken * @param {string} billingType - type of billing agreement * @param {string} billingDesription - billing agreement description * @param {string} returnUrl - success callback url * @param {string} cancelUrl - cancel callback url * @returns {Promise<PaypalECResultType>} promise of api call result */ getReferenceAuthToken(billingType: string, billingDesription: string, returnUrl: string, cancelUrl: string): Promise<PaypalECResultType> { const params: Array<PaypalECPaymentType> = [{ PAYMENTACTION: 'AUTHORIZATION', AMT: 0 }]; return this.setExpressCheckout(params, returnUrl, cancelUrl, { L_BILLINGTYPE0: billingType, L_BILLINGAGREEMENTDESCRIPTION0: billingDesription, VERSION: '86' }); } /** * @method createBillingAgreement * @param {string} token - auth token created from getBillingAuthorizationToken * @returns {Promise<PaypalECResultType>} promise of api call result */ createBillingAgreement(token: string): Promise<PaypalECResultType> { const params: Object = { TOKEN: token }; return this.call('CreateBillingAgreement', params); } /** * @method doReferenceTransaction * @param {string} amount - money to capture from customer's account * @param {string} paymentAction - type of payment * @param {referenceID} - billing id created with createBillingAgreement * @returns {Promise<PaypalECResultType>} promise of api call result */ doReferenceTransaction(amount: number, paymentAction: string, referenceID: string): Promise<PaypalECResultType> { const params: Object = { AMT: amount, PAYMENTACTION: paymentAction, REFERENCEID: referenceID }; return this.call('DoReferenceTransaction', params); } }