Spaces:
Sleeping
Sleeping
File size: 7,766 Bytes
f4866cb 463569a f4866cb ca1cc57 f4866cb ca1cc57 463569a ca1cc57 463569a f4866cb d18f023 f4866cb d18f023 f4866cb 463569a f4866cb d18f023 f4866cb d18f023 f4866cb d18f023 f4866cb 6b95a9f ca1cc57 463569a 6b95a9f ca1cc57 6b95a9f 463569a ca1cc57 6b95a9f ca1cc57 463569a ca1cc57 af4a850 ca1cc57 463569a ca1cc57 463569a ca1cc57 463569a ca1cc57 463569a ca1cc57 463569a ca1cc57 463569a ca1cc57 463569a ca1cc57 6b95a9f ca1cc57 6b95a9f 463569a ca1cc57 463569a 6b95a9f ca1cc57 6b95a9f 463569a ca1cc57 463569a 6b95a9f f4866cb ca1cc57 6b95a9f f4866cb 6b95a9f ca1cc57 463569a ca1cc57 463569a ca1cc57 463569a ca1cc57 f4866cb |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
// payment.service.ts
import { forwardRef, HttpStatus, Inject, Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import * as querystring from 'qs';
import * as crypto from 'crypto';
import { OrderService } from '../modules/order/order.service.js';
import { PaymentEntity } from '../entities/payment.entity.js';
import { CreatePaymentDto } from './dto/create-payment.dto..js';
import { VnpCardType } from '../common/enums/VnpCardType.enum.js';
@Injectable()
export class PaymentService {
constructor(
@Inject(forwardRef(() => OrderService))
private readonly orderService: OrderService,
private readonly configService: ConfigService,
) {}
async createPaymentUrl(
amount: number,
orderId: string,
orderDescription: string,
orderType: string,
language: string,
ipAddr: string,
) {
const tmnCode = this.configService.get<string>('vnp_TmnCode');
const secretKey = this.configService.get<string>('vnp_HashSecret');
const vnpUrl = this.configService.get<string>('vnp_Url');
const returnUrl = this.configService.get<string>('vnp_ReturnUrl');
const date = new Date();
const createDate = this.formatDate(date, 'yyyymmddHHmmss');
const locale = language || 'vn';
const currCode = 'VND';
const vnp_Params: Record<string, string> = {
vnp_Version: '2.1.0',
vnp_Command: 'pay',
vnp_TmnCode: tmnCode,
vnp_Locale: locale,
vnp_CurrCode: currCode,
vnp_TxnRef: orderId,
vnp_OrderInfo: orderDescription,
vnp_OrderType: orderType,
vnp_Amount: (amount * 100).toString(),
vnp_ReturnUrl: returnUrl,
vnp_IpAddr: ipAddr,
vnp_CreateDate: createDate,
};
console.log('3');
const sortedParams = this.sortObject(vnp_Params);
// Sign the data
const signData = querystring.stringify(sortedParams, { encode: false });
const hmac = crypto.createHmac('sha512', secretKey);
const signed = hmac.update(Buffer.from(signData, 'utf-8')).digest('hex');
sortedParams['vnp_SecureHash'] = signed;
// Create the URL
const res = `${vnpUrl}?${querystring.stringify(sortedParams, { encode: false })}`;
return res;
}
async vnpayIpn(reqQuery) {
console.log('helloooo');
let vnp_Params = reqQuery;
let secureHash = vnp_Params['vnp_SecureHash'];
let orderId = vnp_Params['vnp_TxnRef'];
let rspCode = vnp_Params['vnp_ResponseCode'];
delete vnp_Params['vnp_SecureHash'];
delete vnp_Params['vnp_SecureHashType'];
vnp_Params = this.sortObject(vnp_Params);
let secretKey = this.configService.get('vnp_HashSecret');
let signData = querystring.stringify(vnp_Params, { encode: false });
let hmac = crypto.createHmac('sha512', secretKey);
let signed = hmac.update(Buffer.from(signData, 'utf-8')).digest('hex');
let paymentStatus = '0'; // Giả sử '0' là trạng thái khởi tạo giao dịch, chưa có IPN. Trạng thái này được lưu khi yêu cầu thanh toán chuyển hướng sang Cổng thanh toán VNPAY tại đầu khởi tạo đơn hàng.
//let paymentStatus = '1'; // Giả sử '1' là trạng thái thành công bạn cập nhật sau IPN được gọi và trả kết quả về nó
//let paymentStatus = '2'; // Giả sử '2' là trạng thái thất bại bạn cập nhật sau IPN được gọi và trả kết quả về nó
//Kiểm tra có đúng order id không
let checkOrderId = true;
let order;
try {
order = await this.orderService.findOne(orderId);
} catch (error) {
return {
statusCode: HttpStatus.OK,
message: 'Order not found',
};
}
console.log('order = ', order);
console.log('order total value ', order.total_value);
// Kiểm tra số tiền "giá trị của vnp_Amout/100" trùng khớp với số tiền của đơn hàng trong CSDL của bạn
let checkAmount =
order.total_value == parseFloat(vnp_Params['vnp_Amount']) / 100;
if (secureHash === signed) {
//kiểm tra checksum
if (checkOrderId) {
if (checkAmount) {
if (paymentStatus == '0') {
//kiểm tra tình trạng giao dịch trước khi cập nhật tình trạng thanh toán
if (rspCode == '00') {
//thanh cong
//paymentStatus = '1'
// Ở đây cập nhật trạng thái giao dịch thanh toán thành công vào CSDL của bạn
const payment = await this.create({
payment_method: 2,
vnp_amount: parseFloat(vnp_Params['vnp_Amount']) / 100,
vnp_bank_code: vnp_Params['vnp_BankCode'],
vnp_bank_tran_no: vnp_Params['vnp_BankTranNo'],
vnp_card_type:
VnpCardType[
vnp_Params['vnp_CardType'] as keyof typeof VnpCardType
],
vnp_order_info: vnp_Params['vnp_BankTranNo'],
vnp_paydate: vnp_Params['vnp_PayDate'],
vnp_response_code: vnp_Params['vnp_ResponseCode'],
vnp_transaction_no: vnp_Params['vnp_TransactionNo'],
vnp_transaction_status: vnp_Params['vnp_TransactionStatus'],
});
await PaymentEntity.save(payment);
this.orderService.updateOrderPayment(orderId, payment.id);
return {
statusCode: HttpStatus.OK,
message: 'Thành công!',
};
} else {
//that bai
//paymentStatus = '2'
// Ở đây cập nhật trạng thái giao dịch thanh toán thất bại vào CSDL của bạn
return {
statusCode: HttpStatus.OK,
message: 'Thất bại',
};
}
} else {
return {
statusCode: HttpStatus.OK,
message: 'This order has been updated to the payment status',
};
}
} else {
return {
statusCode: HttpStatus.OK,
message: 'Amount invalid',
};
}
} else {
return {
statusCode: HttpStatus.OK,
message: 'Order not found',
};
}
} else {
return {
statusCode: HttpStatus.OK,
message: 'Checksum failed!',
};
}
}
// Format date helper function
formatDate(date: Date, format: string): string {
const yyyymmdd = date.toISOString().slice(0, 10).replace(/-/g, ''); // YYYYMMDD
const hhmmss = date.toTimeString().slice(0, 8).replace(/:/g, ''); // HHMMSS
return format === 'yyyymmddHHmmss' ? yyyymmdd + hhmmss : hhmmss;
}
sortObject(obj) {
let sorted = {};
let str = [];
let key;
for (key in obj) {
if (obj.hasOwnProperty(key)) {
str.push(encodeURIComponent(key));
}
}
str.sort();
for (key = 0; key < str.length; key++) {
sorted[str[key]] = encodeURIComponent(obj[str[key]]).replace(/%20/g, '+');
}
return sorted;
}
async create(
createPaymentDto: CreatePaymentDto,
): Promise<PaymentEntity | undefined> {
return PaymentEntity.create({
payment_method: 2,
vnp_amount: createPaymentDto.vnp_amount,
vnp_bank_code: createPaymentDto.vnp_bank_code,
vnp_bank_tran_no: createPaymentDto.vnp_bank_tran_no,
vnp_card_type: createPaymentDto.vnp_card_type,
vnp_order_info: createPaymentDto.vnp_order_info,
vnp_paydate: createPaymentDto.vnp_paydate,
vnp_response_code: createPaymentDto.vnp_response_code,
vnp_transaction_no: createPaymentDto.vnp_transaction_no,
vnp_transaction_status: createPaymentDto.vnp_transaction_status,
});
}
}
|