MicProtocol / apps /api /dist /billing /paystack.service.js
Benaah's picture
Upload folder using huggingface_hub
303c85f verified
Raw History Blame Contribute Delete
13.2 kB
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var PaystackService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.PaystackService = void 0;
const common_1 = require("@nestjs/common");
const config_1 = require("@nestjs/config");
let PaystackService = PaystackService_1 = class PaystackService {
configService;
logger = new common_1.Logger(PaystackService_1.name);
secretKey;
publicKey;
baseUrl = 'https://api.paystack.co';
callbackUrl;
constructor(configService) {
this.configService = configService;
this.secretKey = this.configService.get('PAYSTACK_SECRET_KEY') || '';
this.publicKey = this.configService.get('PAYSTACK_PUBLIC_KEY') || '';
this.callbackUrl = this.configService.get('PAYSTACK_CALLBACK_URL') || '';
if (!this.secretKey) {
this.logger.warn('PAYSTACK_SECRET_KEY is not configured');
}
}
isConfigured() {
return Boolean(this.secretKey);
}
getPublicKey() {
return this.publicKey;
}
getHeaders() {
return {
Authorization: `Bearer ${this.secretKey}`,
'Content-Type': 'application/json',
};
}
async initializeTransaction(input) {
if (!this.secretKey) {
throw new Error('Paystack is not configured');
}
const body = {
email: input.email,
amount: Math.round(input.amount),
reference: input.reference,
};
if (input.currency)
body.currency = input.currency;
if (input.callbackUrl)
body.callback_url = input.callbackUrl;
if (input.metadata)
body.metadata = JSON.stringify(input.metadata);
if (input.channels?.length)
body.channels = input.channels;
const response = await fetch(`${this.baseUrl}/transaction/initialize`, {
method: 'POST',
headers: this.getHeaders(),
body: JSON.stringify(body),
});
const payload = (await response.json());
if (!response.ok || !payload.status || !payload.data) {
this.logger.error(`Paystack initialize failed: ${JSON.stringify(payload)}`);
throw new Error(payload.message || 'Failed to initialize Paystack transaction');
}
return {
authorizationUrl: payload.data.authorization_url,
accessCode: payload.data.access_code,
reference: payload.data.reference,
};
}
async createMobileMoneyCharge(input) {
if (!this.secretKey) {
throw new Error('Paystack is not configured');
}
const body = {
email: input.email,
amount: Math.round(input.amount),
reference: input.reference,
mobile_money: {
phone: this.normalizeMobileMoneyPhone(input.mobileMoney.phone),
provider: input.mobileMoney.provider,
},
};
if (input.currency)
body.currency = input.currency;
if (input.callbackUrl)
body.callback_url = input.callbackUrl;
if (input.metadata)
body.metadata = JSON.stringify(input.metadata);
const response = await fetch(`${this.baseUrl}/charge`, {
method: 'POST',
headers: this.getHeaders(),
body: JSON.stringify(body),
});
const payload = (await response.json());
if (!response.ok || !payload.status || !payload.data) {
this.logger.error(`Paystack mobile money charge failed: ${JSON.stringify(payload)}`);
throw new Error(payload.message || 'Failed to initiate Paystack mobile money charge');
}
return {
status: payload.data.status,
reference: payload.data.reference,
displayMessage: payload.data.display_message,
providerReference: payload.data.provider_reference,
};
}
async verifyTransaction(reference) {
if (!this.secretKey) {
throw new Error('Paystack is not configured');
}
const response = await fetch(`${this.baseUrl}/transaction/verify/${encodeURIComponent(reference)}`, { method: 'GET', headers: this.getHeaders() });
const payload = (await response.json());
if (!response.ok || !payload.status || !payload.data) {
this.logger.error(`Paystack verify failed: ${JSON.stringify(payload)}`);
throw new Error(payload.message || 'Failed to verify Paystack transaction');
}
return {
status: payload.data.status,
amount: payload.data.amount,
currency: payload.data.currency,
reference: payload.data.reference,
channel: payload.data.channel,
paidAt: payload.data.paid_at ?? null,
gatewayResponse: payload.data.gateway_response,
authorizationCode: payload.data.authorization?.authorization_code,
};
}
verifyWebhookSignature(rawBody, signatureHeader) {
if (!this.secretKey || !signatureHeader)
return false;
const { createHmac } = require('crypto');
const expected = createHmac('sha512', this.secretKey).update(rawBody).digest('hex');
if (expected.length !== signatureHeader.length)
return false;
let mismatch = 0;
for (let i = 0; i < expected.length; i++) {
mismatch |= expected.charCodeAt(i) ^ signatureHeader.charCodeAt(i);
}
return mismatch === 0;
}
async createPlan(input) {
if (!this.secretKey)
throw new Error('Paystack is not configured');
const body = {
name: input.name,
amount: Math.round(input.amount),
interval: input.interval,
};
if (input.currency)
body.currency = input.currency;
if (input.description)
body.description = input.description;
if (input.planCode)
body.plan_code = input.planCode;
const response = await fetch(`${this.baseUrl}/plan`, {
method: 'POST',
headers: this.getHeaders(),
body: JSON.stringify(body),
});
const payload = (await response.json());
if (!response.ok || !payload.status || !payload.data) {
this.logger.error(`Paystack plan creation failed: ${JSON.stringify(payload)}`);
throw new Error(payload.message || 'Failed to create Paystack plan');
}
return payload.data;
}
async fetchPlan(idOrCode) {
if (!this.secretKey)
throw new Error('Paystack is not configured');
const response = await fetch(`${this.baseUrl}/plan/${encodeURIComponent(idOrCode)}`, { method: 'GET', headers: this.getHeaders() });
const payload = (await response.json());
if (!response.ok || !payload.status || !payload.data)
return null;
return payload.data;
}
async createSubscription(input) {
if (!this.secretKey)
throw new Error('Paystack is not configured');
const body = {
customer: input.customerCode,
plan: input.planCode,
};
if (input.authorizationCode)
body.authorization = input.authorizationCode;
if (input.startDate)
body.start_date = input.startDate;
const response = await fetch(`${this.baseUrl}/subscription`, {
method: 'POST',
headers: this.getHeaders(),
body: JSON.stringify(body),
});
const payload = (await response.json());
if (!response.ok || !payload.status || !payload.data) {
this.logger.error(`Paystack subscription creation failed: ${JSON.stringify(payload)}`);
throw new Error(payload.message || 'Failed to create Paystack subscription');
}
return payload.data;
}
async fetchSubscription(idOrCode) {
if (!this.secretKey)
throw new Error('Paystack is not configured');
const response = await fetch(`${this.baseUrl}/subscription/${encodeURIComponent(idOrCode)}`, { method: 'GET', headers: this.getHeaders() });
const payload = (await response.json());
if (!response.ok || !payload.status || !payload.data)
return null;
return payload.data;
}
async disableSubscription(subscriptionCode, token) {
if (!this.secretKey)
throw new Error('Paystack is not configured');
const response = await fetch(`${this.baseUrl}/subscription/disable`, {
method: 'POST',
headers: this.getHeaders(),
body: JSON.stringify({
code: subscriptionCode,
token,
}),
});
const payload = (await response.json());
if (!response.ok || !payload.status || !payload.data) {
this.logger.error(`Paystack subscription disable failed: ${JSON.stringify(payload)}`);
throw new Error(payload.message || 'Failed to disable Paystack subscription');
}
return payload.data;
}
async enableSubscription(subscriptionCode, token) {
if (!this.secretKey)
throw new Error('Paystack is not configured');
const response = await fetch(`${this.baseUrl}/subscription/enable`, {
method: 'POST',
headers: this.getHeaders(),
body: JSON.stringify({
code: subscriptionCode,
token,
}),
});
const payload = (await response.json());
if (!response.ok || !payload.status || !payload.data) {
this.logger.error(`Paystack subscription enable failed: ${JSON.stringify(payload)}`);
throw new Error(payload.message || 'Failed to enable Paystack subscription');
}
return payload.data;
}
async refundTransaction(reference, amountInSubunits) {
if (!this.secretKey)
throw new Error('Paystack is not configured');
const body = { transaction: reference };
if (typeof amountInSubunits === 'number')
body.amount = amountInSubunits;
const response = await fetch(`${this.baseUrl}/refund`, {
method: 'POST',
headers: this.getHeaders(),
body: JSON.stringify(body),
});
const payload = (await response.json());
if (!response.ok || !payload.status || !payload.data) {
this.logger.error(`Paystack refund failed: ${JSON.stringify(payload)}`);
throw new Error(payload.message || 'Failed to refund Paystack transaction');
}
return payload.data;
}
async createCustomer(input) {
if (!this.secretKey)
throw new Error('Paystack is not configured');
const body = { email: input.email };
if (input.firstName)
body.first_name = input.firstName;
if (input.lastName)
body.last_name = input.lastName;
if (input.phone)
body.phone = input.phone;
if (input.metadata)
body.metadata = input.metadata;
const response = await fetch(`${this.baseUrl}/customer`, {
method: 'POST',
headers: this.getHeaders(),
body: JSON.stringify(body),
});
const payload = (await response.json());
if (!response.ok || !payload.status || !payload.data) {
this.logger.error(`Paystack customer creation failed: ${JSON.stringify(payload)}`);
throw new Error(payload.message || 'Failed to create Paystack customer');
}
return payload.data;
}
normalizeMobileMoneyPhone(phone) {
const digits = phone.replace(/\D/g, '');
if (digits.startsWith('254') && digits.length === 12) {
return `0${digits.slice(3)}`;
}
if (digits.startsWith('0') && digits.length === 10) {
return digits;
}
if (digits.length === 9) {
return `0${digits}`;
}
throw new Error('Invalid phone number. Use 07XX, 01XX, or 2547XXXXXXXX format.');
}
};
exports.PaystackService = PaystackService;
exports.PaystackService = PaystackService = PaystackService_1 = __decorate([
(0, common_1.Injectable)(),
__metadata("design:paramtypes", [config_1.ConfigService])
], PaystackService);
//# sourceMappingURL=paystack.service.js.map