@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
JavaScript
"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