mn-payment
Version:
194 lines (165 loc) • 4.66 kB
text/typescript
import { SaveToken, Token } from "./types";
type CreateInvoiceRequestInput = {
sender_invoice_no: string;
invoice_receiver_code: string;
invoice_description: string;
amount: number;
callback_url: string;
};
type TokenResponse = {
token_type: string;
refresh_expires_in: number;
refresh_token: string;
access_token: string;
expires_in: number;
scope: string;
"not-before-policy": string;
session_state: string;
};
type InvoiceResponse = {
invoice_id: string;
qr_text: string;
qr_image: string;
qPay_shortUrl: string;
urls: {
name: string;
description: string;
logo: string;
link: string;
}[];
};
type InvoiceCheckResponse = {
count: number;
paid_amount: number;
rows: Array<{
payment_id: string;
payment_status: string;
payment_amount: string;
trx_fee: string;
payment_currency: string;
payment_wallet: string;
payment_type: string;
next_payment_date: any;
next_payment_datetime: any;
card_transactions: Array<any>;
p2p_transactions: Array<{
id: string;
transaction_bank_code: string;
account_bank_code: string;
account_bank_name: string;
account_number: string;
status: string;
amount: string;
currency: string;
settlement_status: string;
}>;
}>;
};
export class Qpay {
private username: string;
private password: string;
private invoiceCode: string;
private saveToken?: SaveToken
private baseUrl = "https://merchant.qpay.mn";
constructor(username: string, password: string, invoiceCode: string, saveToken?: SaveToken) {
this.username = username;
this.password = password;
this.invoiceCode = invoiceCode;
this.saveToken = saveToken;
}
private async login() {
const req = await fetch(`${this.baseUrl}/v2/auth/token`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Basic ${btoa(`${this.username}:${this.password}`)}`,
},
});
const res = await req.json();
if (res.error) {
throw new Error(res.message);
}
const response = res as TokenResponse
return {
access_token: response.access_token,
refresh_token: response.refresh_token,
expires_in: response.expires_in,
refresh_expires_in: response.refresh_expires_in,
} satisfies Token
}
private async getRefreshToken(refreshToken: string) {
const req = await fetch(`${this.baseUrl}/v2/auth/refresh`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${refreshToken}`,
},
});
const res = await req.json();
if (res.error) {
throw new Error(res.message);
}
const response = res as TokenResponse
return {
access_token: response.access_token,
refresh_token: response.refresh_token,
expires_in: response.expires_in,
refresh_expires_in: response.refresh_expires_in,
} satisfies Token
}
private async checkToken(inputToken?: Token) {
let token = inputToken
if (!token || !token.refresh_expires_in) {
token = await this.login()
}
if (token.refresh_expires_in && new Date(token.refresh_expires_in * 1000) <= new Date()) {
token = await this.login();
} else if (
new Date(token.expires_in * 1000) <= new Date()
) {
token = await this.getRefreshToken(token.refresh_token);
}
if (this.saveToken && JSON.stringify(inputToken) !== JSON.stringify(token)) {
await this.saveToken(token);
}
return token;
}
async createInvoice(input: CreateInvoiceRequestInput, token?: Token) {
token = await this.checkToken(token);
const req = await fetch(`${this.baseUrl}/v2/invoice`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token.access_token}`,
},
body: JSON.stringify({
...input,
invoice_code: this.invoiceCode,
}),
});
const res: InvoiceResponse = await req.json();
return {
data: res,
token,
};
}
async checkInvoice(invoiceId: string, token?: Token) {
token = await this.checkToken(token);
const req = await fetch(`${this.baseUrl}/v2/payment/check`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token.access_token}`,
},
body: JSON.stringify({
object_type: "INVOICE",
object_id: invoiceId,
}),
});
const res: InvoiceCheckResponse = await req.json();
return {
data: res,
token,
};
}
}