payme-integration
Version:
NestJS integration module for Payme payment system
329 lines (328 loc) • 13.7 kB
JavaScript
;
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 __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PaymeService = void 0;
const common_1 = require("@nestjs/common");
const transaction_methods_1 = require("./types/transaction-methods");
const errors_1 = require("../errors");
const luxon_1 = require("luxon");
const enums_1 = require("./types/enums");
const mongoose_1 = require("@nestjs/mongoose");
const payme_transaction_schema_1 = require("./schemas/payme-transaction.schema");
const mongoose_2 = require("mongoose");
let PaymeService = class PaymeService {
constructor(paymeStrategy, transactionModel) {
this.paymeStrategy = paymeStrategy;
this.transactionModel = transactionModel;
}
async handleTransactionMethods(reqBody) {
const method = reqBody.method;
switch (method) {
case transaction_methods_1.TransactionMethods.CheckPerformTransaction:
return this.checkPerformTransaction(reqBody);
case transaction_methods_1.TransactionMethods.CreateTransaction:
return this.createTransaction(reqBody);
case transaction_methods_1.TransactionMethods.PerformTransaction:
return this.performTransaction(reqBody);
case transaction_methods_1.TransactionMethods.CancelTransaction:
return this.cancelTransaction(reqBody);
case transaction_methods_1.TransactionMethods.CheckTransaction:
return this.checkTransaction(reqBody);
case transaction_methods_1.TransactionMethods.GetStatement:
return this.getStatement(reqBody);
default:
throw new errors_1.PaymeException(errors_1.PaymeError.METHOD_NOT_FOUND);
}
}
async checkPerformTransaction(dto) {
const order = await this.paymeStrategy.getOrderByAccount(dto.params.account);
if (!order) {
throw new errors_1.PaymeException(errors_1.PaymeError.ORDER_NOT_FOUND, dto.params.account);
}
if (order.status !== enums_1.OrderStatus.ReadyForPayment) {
throw new errors_1.PaymeException(errors_1.PaymeError.ORDER_IN_PROGRESS);
}
if (order.amount * 100 !== dto.params.amount) {
throw new errors_1.PaymeException(errors_1.PaymeError.INVALID_AMOUNT);
}
return {
result: {
allow: true,
},
id: dto.id,
};
}
async createTransaction(dto) {
const transId = dto.params.id;
const transaction = await this.transactionModel
.findOne({
trans_id: transId,
})
.exec();
if (!transaction) {
const pending_transaction = await this.transactionModel
.findOne({
state: enums_1.TransactionState.Pending,
account: dto.params.account,
})
.exec();
if (pending_transaction) {
throw new errors_1.PaymeException(errors_1.PaymeError.TRANSACTION_ALREADY_CREATED);
}
await this.checkPerformTransaction({
id: dto.id,
method: transaction_methods_1.TransactionMethods.CheckPerformTransaction,
params: {
account: dto.params.account,
amount: dto.params.amount,
},
});
const newTransaction = await this.transactionModel.create({
state: enums_1.TransactionState.Pending,
trans_id: dto.params.id,
amount: dto.params.amount,
account: dto.params.account,
create_time: new Date(),
});
return {
result: {
transaction: newTransaction._id.toHexString(),
create_time: newTransaction.create_time.getTime(),
state: newTransaction.state,
},
id: dto.id,
};
}
else {
if (transaction.state !== enums_1.TransactionState.Pending) {
throw new errors_1.PaymeException(errors_1.PaymeError.INACTIVE_TRANSCACTION);
}
if (this.checkTransactionExpiration(transaction.create_time)) {
transaction.state = enums_1.TransactionState.PendingCanceled;
transaction.reason = enums_1.TransactionCancellationReason.TimeoutCancellation;
transaction.cancel_time = new Date();
await transaction.save();
throw new errors_1.PaymeException(errors_1.PaymeError.COULD_NOT_PERFORM_TRANSACTION);
}
return {
result: {
transaction: transaction._id.toHexString(),
create_time: transaction.create_time?.getTime() ?? 0,
state: transaction.state,
},
id: dto.id,
};
}
}
async performTransaction(dto) {
const transaction = await this.transactionModel
.findOne({
trans_id: dto.params.id,
})
.exec();
if (!transaction) {
throw new errors_1.PaymeException(errors_1.PaymeError.TRANSACTION_NOT_FOUND);
}
if (transaction.state === enums_1.TransactionState.Pending) {
const isTransactionExpired = this.checkTransactionExpiration(transaction.create_time);
if (isTransactionExpired) {
transaction.state = enums_1.TransactionState.PendingCanceled;
transaction.reason = enums_1.TransactionCancellationReason.TimeoutCancellation;
transaction.cancel_time = new Date();
await transaction.save();
throw new errors_1.PaymeException(errors_1.PaymeError.COULD_NOT_PERFORM_TRANSACTION);
}
else {
const perform_time = new Date();
const updated_payment = await this.transactionModel
.findOneAndUpdate({
trans_id: dto.params.id,
}, {
state: enums_1.TransactionState.Paid,
perform_time,
status: enums_1.TransactionStatus.PAID,
}, {
new: true,
})
.exec();
if (!updated_payment) {
throw new errors_1.PaymeException(errors_1.PaymeError.TRANSACTION_NOT_FOUND);
}
await this.paymeStrategy.successPayment(updated_payment);
return {
result: {
transaction: updated_payment.id,
perform_time: updated_payment.perform_time?.getTime() ?? 0,
state: updated_payment.state,
},
id: dto.id,
};
}
}
else if (transaction.state === enums_1.TransactionState.Paid) {
return {
result: {
transaction: transaction.id,
perform_time: transaction.perform_time?.getTime() ?? 0,
state: transaction.state,
},
id: dto.id,
};
}
else {
throw new errors_1.PaymeException(errors_1.PaymeError.COULD_NOT_PERFORM_TRANSACTION);
}
}
async cancelTransaction(dto) {
const transaction = await this.transactionModel
.findOne({
trans_id: dto.params.id,
})
.exec();
if (!transaction) {
throw new errors_1.PaymeException(errors_1.PaymeError.TRANSACTION_NOT_FOUND);
}
if (transaction.state === enums_1.TransactionState.Pending) {
const cancel_transaction = await this.transactionModel
.findOneAndUpdate({
_id: transaction._id,
}, {
state: enums_1.TransactionState.PendingCanceled,
reason: dto.params.reason,
cancel_time: new Date(),
status: enums_1.TransactionStatus.CANCELED,
}, { new: true })
.exec();
if (!cancel_transaction) {
throw new errors_1.PaymeException(errors_1.PaymeError.TRANSACTION_NOT_FOUND);
}
return {
result: {
transaction: cancel_transaction._id.toHexString(),
state: cancel_transaction.state,
cancel_time: cancel_transaction?.cancel_time?.getTime() ?? 0,
},
id: dto.id,
};
}
else if (transaction.state !== enums_1.TransactionState.Paid) {
return {
result: {
transaction: transaction._id.toHexString(),
state: transaction.state,
cancel_time: transaction.cancel_time?.getTime() ?? 0,
},
id: dto.id,
};
}
else {
const updated_transaction = await this.transactionModel
.findOneAndUpdate({
trans_id: dto.params.id,
}, {
status: enums_1.TransactionStatus.CANCELED,
state: enums_1.TransactionState.PaidCanceled,
cancel_time: new Date(),
reason: dto.params.reason,
}, {
new: true,
})
.exec();
if (!updated_transaction) {
throw new errors_1.PaymeException(errors_1.PaymeError.TRANSACTION_NOT_FOUND);
}
return {
id: dto.id,
result: {
transaction: updated_transaction._id.toHexString(),
state: updated_transaction.state,
cancel_time: updated_transaction?.cancel_time?.getTime() ?? 0,
},
};
}
}
async checkTransaction(dto) {
const transaction = await this.transactionModel
.findOne({
trans_id: dto.params.id,
})
.exec();
if (!transaction) {
throw new errors_1.PaymeException(errors_1.PaymeError.TRANSACTION_NOT_FOUND);
}
return {
result: {
create_time: transaction.create_time?.getTime() ?? 0,
perform_time: transaction.perform_time
? new Date(transaction.perform_time).getTime()
: 0,
cancel_time: transaction?.cancel_time
? new Date(transaction.cancel_time).getTime()
: 0,
transaction: transaction._id.toHexString(),
state: transaction.state,
reason: transaction.reason ?? null,
},
id: dto.id,
};
}
async getStatement(dto) {
const transactions = await this.transactionModel
.find({
create_time: {
$gte: dto.params.from,
$lte: dto.params.to,
},
})
.exec();
const mappedTransactions = transactions.map((transaction) => {
return {
id: transaction._id.toHexString(),
account: transaction.account,
amount: transaction.amount,
time: transaction?.create_time?.getTime() ?? 0,
transaction: transaction.id,
state: transaction.state,
perform_time: transaction.perform_time?.getTime() ?? 0,
cancel_time: transaction.cancel_time?.getTime() ?? 0,
create_time: transaction.create_time?.getTime() ?? 0,
reason: transaction.reason ?? null,
};
});
// some logic
return {
result: {
transactions: mappedTransactions,
},
id: dto.id,
};
}
checkTransactionExpiration(createdAt) {
const transactionCreatedAt = new Date(createdAt);
const timeoutDuration = 720;
const timeoutThreshold = luxon_1.DateTime.now()
.minus({
minutes: timeoutDuration,
})
.toJSDate();
return transactionCreatedAt < timeoutThreshold;
}
};
exports.PaymeService = PaymeService;
exports.PaymeService = PaymeService = __decorate([
(0, common_1.Injectable)(),
__param(0, (0, common_1.Inject)('PAYME_STRATEGY')),
__param(1, (0, mongoose_1.InjectModel)(payme_transaction_schema_1.PaymeTransaction.name)),
__metadata("design:paramtypes", [Object, mongoose_2.Model])
], PaymeService);