UNPKG

@sonatel-os/juf

Version:

The community SDK for Orange Money, SMS, Email & Sonatel APIs on the Orange Developer Platform.

350 lines (325 loc) 15.8 kB
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _chunkUD6WYISCcjs = require('./chunk-UD6WYISC.cjs'); var _chunkTSJ7NWVTcjs = require('./chunk-TSJ7NWVT.cjs'); var _chunkW5WEVJMXcjs = require('./chunk-W5WEVJMX.cjs'); // src/payment/payment.structure.js var _superstruct = require('superstruct'); var Merchant = _superstruct.object.call(void 0, { code: _superstruct.number.call(void 0, ), sitename: _superstruct.string.call(void 0, ) }); var Bill = _superstruct.object.call(void 0, { amount: _superstruct.number.call(void 0, ), reference: _superstruct.string.call(void 0, ) }); var Urls = _superstruct.object.call(void 0, { failed: _superstruct.optional.call(void 0, _superstruct.defaulted.call(void 0, _superstruct.string.call(void 0, ), null)), cancel: _superstruct.optional.call(void 0, _superstruct.defaulted.call(void 0, _superstruct.string.call(void 0, ), null)), success: _superstruct.optional.call(void 0, _superstruct.defaulted.call(void 0, _superstruct.string.call(void 0, ), null)), callback: _superstruct.optional.call(void 0, _superstruct.defaulted.call(void 0, _superstruct.string.call(void 0, ), null)) }); var CheckoutPaymentStructure = _superstruct.object.call(void 0, { merchant: Merchant, bill: Bill, urls: Urls }); var Metadata = _superstruct.define.call(void 0, "object", (value) => { return typeof value === "object" && value !== null; }); var Validity = _superstruct.optional.call(void 0, _superstruct.number.call(void 0, )); var QRCodePaymentStructure = _superstruct.object.call(void 0, { merchant: Merchant, bill: Bill, urls: _superstruct.optional.call(void 0, Urls), metadata: _superstruct.optional.call(void 0, Metadata), validity: Validity }); var QRCodeDecodePaymentStructure = _superstruct.object.call(void 0, { id: _superstruct.string.call(void 0, ) }); // src/payment/qrCodeDecoder.js var QRCodeDecoder = class _QRCodeDecoder { /** @private @type {import('axios').AxiosInstance} */ #client; /** @private @type {string} */ #authorization; /** @private */ #logger; /** * Creates a QRCodeDecoder instance with injectable dependencies. * * @param {object} deps - Dependencies. * @param {import('axios').AxiosInstance} deps.client - HTTP client instance. * @param {string} deps.authorization - Static SP authorization header value. * @param {object} deps.logger - Logger instance. */ constructor({ client, authorization, logger: logger2 }) { this.#client = client; this.#authorization = authorization; this.#logger = logger2; } /** * Factory method to initialize QRCodeDecoder with default dependencies. * @returns {QRCodeDecoder} An initialized instance. */ static init() { const apigeeConfig = _chunkW5WEVJMXcjs.envConfig.get("apigee"); return new _QRCodeDecoder({ client: _chunkW5WEVJMXcjs.requester_default.bootstrap({ baseURL: _chunkW5WEVJMXcjs.getApiUrl.call(void 0, ) }), authorization: apigeeConfig.decode_qr_sp_authorization, logger: _chunkW5WEVJMXcjs.logger.child("qr-decoder") }); } /** * Decodes a QR code by its ID. * * @async * @param {object} qrDetails - The details required to decode the QR code. * @param {string} qrDetails.id - The unique identifier of the QR code. * @returns {Promise<{ id: string, content: { merchantCode: string, merchantName: string, amount: number, reference: string, scope: string, type: string, metadata: object } }>} * @throws {import('../core/errors.js').ValidationError} When input validation fails. * @throws {import('../core/errors.js').ExternalServiceError} When the API request fails. * * @example * const { content } = await decoder.decode({ id: 'doyaT9sH3rGFph_ZuKIs' }); * console.log(content.amount, content.reference); */ async decode({ id }) { _chunkTSJ7NWVTcjs.validate.call(void 0, { id }, QRCodeDecodePaymentStructure, "decodeQrCode"); try { const { data } = await this.#client.get(_chunkTSJ7NWVTcjs.DECODE_QR_URI.replace(":id", id), { headers: { authorization: this.#authorization } }); return data; } catch (error) { if (_optionalChain([error, 'access', _ => _.code, 'optionalAccess', _2 => _2.startsWith, 'call', _3 => _3("JUF_")])) throw error; this.#logger.error("QR code decoding failed", { qrId: id, status: _optionalChain([error, 'access', _4 => _4.response, 'optionalAccess', _5 => _5.status]) }); throw _chunkW5WEVJMXcjs.fromAxiosError.call(void 0, error, "Error decoding QR code."); } } }; var qrCodeDecoder_default = QRCodeDecoder; // src/payment/paymentService.js var Payment = class _Payment { /** @private @type {import('axios').AxiosInstance} */ #client; /** @private @type {Authentication} */ #authService; /** @private */ #config; /** @private */ #logger; /** @private @type {QRCodeDecoder} */ #qrDecoder; /** * Creates a Payment instance with injectable dependencies. * * @param {object} deps - Dependencies for the payment service. * @param {Authentication} deps.authService - Authentication service instance. * @param {import('axios').AxiosInstance} deps.client - HTTP client instance. * @param {object} deps.config - Apigee configuration (for onProd, onPProd). * @param {object} deps.logger - Logger instance with error/warn/info/debug methods. * @param {QRCodeDecoder} [deps.qrDecoder] - Optional QR code decoder instance. */ constructor({ authService, client, config, logger: logger2, qrDecoder }) { this.#authService = authService; this.#client = client; this.#config = config; this.#logger = logger2; this.#qrDecoder = qrDecoder; } /** * Factory method to initialize Payment service with default dependencies. * @method init * @memberof Service\Payment * @param {object} [deps] - Optional shared dependencies. * @param {Authentication} [deps.authService] - Shared auth instance (avoids duplicate token fetches). * @returns {Payment} An initialized instance of Payment. */ static init({ authService } = {}) { const apigeeConfig = _chunkW5WEVJMXcjs.envConfig.get("apigee"); return new _Payment({ authService: authService || _chunkUD6WYISCcjs.authenticationService_default.init(), client: _chunkW5WEVJMXcjs.requester_default.bootstrap({ baseURL: _chunkW5WEVJMXcjs.getApiUrl.call(void 0, ) }), config: apigeeConfig, logger: _chunkW5WEVJMXcjs.logger.child("payment"), qrDecoder: qrCodeDecoder_default.init() }); } /** * Retrieve Authorization headers from cached or fresh token. * * @private * @returns {Promise<{ Authorization: string }>} Authorization headers. */ async #getAuthHeaders() { const { access_token, token_type } = await this.#authService.debug(); return _chunkTSJ7NWVTcjs.buildAuthHeader.call(void 0, access_token, token_type); } /** * Prepares a payment checkout for the OMPay payment gateway. * * @async * @method preparePaymentCheckout * @memberof Service\Payment * @param {object} paymentDetails - The payment details. * @param {object} paymentDetails.merchant - Merchant information. * @param {number} paymentDetails.merchant.code - The merchant code. * @param {string} paymentDetails.merchant.sitename - The merchant's site name. * @param {object} paymentDetails.bill - Bill information. * @param {number} paymentDetails.bill.amount - The amount to be paid. * @param {string} paymentDetails.bill.reference - The reference for the transaction. * @param {object} paymentDetails.urls - URLs for payment status. * @param {string} [paymentDetails.urls.failed] - URL to redirect if payment fails. * @param {string} [paymentDetails.urls.cancel] - URL to redirect if payment is canceled. * @param {string} [paymentDetails.urls.success] - URL to redirect upon successful payment. * @param {string} [paymentDetails.urls.callback] - Callback URL for payment updates. * @returns {Promise<{link: string, secret: number}>} The checkout link and secret. * @throws {import('../core/errors.js').ValidationError} When input validation fails. * @throws {import('../core/errors.js').ExternalServiceError} When the API request fails. * * @example * payment.preparePaymentCheckout({ * merchant: { code: 123456, sitename: 'your-sitename' }, * bill: { amount: 10, reference: '654321' }, * urls: { * failed: 'https://my.site/failed', * cancel: 'https://my.site/canceled', * success: 'https://my.site/success' * } * }) * .then(console.log) * .catch(console.log) */ async preparePaymentCheckout({ merchant, bill, urls }) { _chunkTSJ7NWVTcjs.validate.call(void 0, { merchant, bill, urls }, CheckoutPaymentStructure, "preparePaymentCheckout"); _chunkTSJ7NWVTcjs.validateUrls.call(void 0, urls); const { code: merchantCode, sitename } = merchant; const { reference, amount } = bill; const { success: successUrl, cancel: cancelUrl, failed: failedUrl, callback: callbackUrl } = urls; try { const { Authorization } = await this.#getAuthHeaders(); try { await this.createPaymentQRCode({ merchant, bill, urls, validity: 10 }); } catch (e) { this.#logger.warn("QR code service unavailable, proceeding with USSD only", { reference }); } const payload = { merchantCode, sitename, amount, reference, onProd: Boolean(this.#config.onProd), onPProd: Boolean(this.#config.onPProd), urls: { ...cancelUrl && { cancelUrl }, ...failedUrl && { failedUrl }, ...successUrl && { successUrl }, ...callbackUrl && { callbackUrl } } }; const { data } = await this.#client.post(_chunkTSJ7NWVTcjs.GENERATE_PAYMENT_LINK_URI, payload, { headers: { Authorization } }); return data; } catch (error) { if (_optionalChain([error, 'access', _6 => _6.code, 'optionalAccess', _7 => _7.startsWith, 'call', _8 => _8("JUF_")])) throw error; this.#logger.error("Payment checkout failed", { reference, status: _optionalChain([error, 'access', _9 => _9.response, 'optionalAccess', _10 => _10.status]) }); throw _chunkW5WEVJMXcjs.fromAxiosError.call(void 0, error, "Service not available. Check your setup and try again."); } } /** * Creates a QR code for a payment. * * @async * @method createPaymentQRCode * @memberof Service\Payment * @param {object} paymentDetails - The details for the QR code. * @param {object} paymentDetails.merchant - Merchant information. * @param {number} paymentDetails.merchant.code - The merchant code. * @param {string} paymentDetails.merchant.sitename - The merchant's site name. * @param {object} paymentDetails.bill - Bill information. * @param {number} paymentDetails.bill.amount - The amount to be paid. * @param {string} paymentDetails.bill.reference - The reference for the transaction. * @param {object} [paymentDetails.urls] - URLs for payment status. * @param {string} [paymentDetails.urls.failed] - URL to redirect if payment fails. * @param {string} [paymentDetails.urls.cancel] - URL to redirect if payment is canceled. * @param {string} [paymentDetails.urls.success] - URL to redirect upon successful payment. * @param {string} [paymentDetails.urls.callback] - Callback URL for payment updates. * @param {object} [paymentDetails.metadata] - Additional metadata for the QR code. * @param {number} [paymentDetails.validity] - Validity period for the QR code in seconds. * @returns {Promise<{ deepLink: string, deepLinks: { MAXIT: string, OM: string }, qrCode: string, validity: number, metadata: object, shortLink: string, qrId: string }>} * @throws {import('../core/errors.js').ValidationError} When input validation fails. * @throws {import('../core/errors.js').ExternalServiceError} When the API request fails. * * @example * payment.createPaymentQRCode({ * merchant: { code: 123456, sitename: 'your-sitename' }, * bill: { amount: 10, reference: '654321' }, * metadata: { myKey: 'value' }, * validity: 300 * }) * .then(console.log) * .catch(console.log) */ async createPaymentQRCode({ merchant, bill, urls = {}, metadata = {}, validity }) { _chunkTSJ7NWVTcjs.validate.call(void 0, { merchant, bill, urls, metadata, validity }, QRCodePaymentStructure, "createPaymentQRCode"); _chunkTSJ7NWVTcjs.validateUrls.call(void 0, urls); const { code: merchantCode, sitename } = merchant; const { reference, amount } = bill; const { success: callbackSuccessUrl, cancel: callbackCancelUrl, failed: callbackFailedUrl, callback: xCallbackUrl } = urls; try { const { Authorization } = await this.#getAuthHeaders(); const payload = { amount: { unit: _chunkW5WEVJMXcjs.CURRENCY_UNIT, value: amount }, callbackCancelUrl: callbackCancelUrl || callbackFailedUrl, callbackSuccessUrl, reference, code: merchantCode, metadata: { ...metadata, reference }, name: sitename, validity }; const headers = { Authorization, ...xCallbackUrl && { "X-Callback-Url": xCallbackUrl } }; const { data } = await this.#client.post(_chunkTSJ7NWVTcjs.GENERATE_QR_URI, payload, { headers }); return data; } catch (error) { if (_optionalChain([error, 'access', _11 => _11.code, 'optionalAccess', _12 => _12.startsWith, 'call', _13 => _13("JUF_")])) throw error; this.#logger.error("QR code generation failed", { reference, status: _optionalChain([error, 'access', _14 => _14.response, 'optionalAccess', _15 => _15.status]) }); throw _chunkW5WEVJMXcjs.fromAxiosError.call(void 0, error, "QR code generation failed. Please try again."); } } /** * Decodes a QR code by its ID. * Delegates to {@link QRCodeDecoder} — a separate service that uses static SP authorization * instead of OAuth2 tokens. Only authorized applications can decode QR codes. * * @async * @method decodeQrCode * @memberof Service\Payment * @param {object} qrDetails - The details required to decode the QR code. * @param {string} qrDetails.id - The unique identifier of the QR code. * @returns {Promise<{ id: string, content: { merchantCode: string, merchantName: string, amount: number, reference: string, scope: string, type: string, metadata: object } }>} * @throws {import('../core/errors.js').ValidationError} When input validation fails. * @throws {import('../core/errors.js').ExternalServiceError} When the API request fails. * * @example * payment.decodeQrCode({ id: 'doyaT9sH3rGFph_ZuKIs' }) * .then(console.log) * .catch(console.log) */ async decodeQrCode({ id }) { return this.#qrDecoder.decode({ id }); } }; var paymentService_default = Payment; exports.CheckoutPaymentStructure = CheckoutPaymentStructure; exports.QRCodePaymentStructure = QRCodePaymentStructure; exports.QRCodeDecodePaymentStructure = QRCodeDecodePaymentStructure; exports.qrCodeDecoder_default = qrCodeDecoder_default; exports.paymentService_default = paymentService_default; //# sourceMappingURL=chunk-7HR2MVLX.cjs.map