UNPKG

@warriorteam/redai-zalo-sdk

Version:

Comprehensive TypeScript/JavaScript SDK for Zalo APIs - Official Account v3.0, ZNS with Full Type Safety, Consultation Service, Broadcast Service, Group Messaging with List APIs, Social APIs, Enhanced Article Management, Promotion Service v3.0 with Multip

417 lines 18.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PurchaseService = void 0; const common_1 = require("../types/common"); const purchase_1 = require("../types/purchase"); /** * Service xử lý các API mua sản phẩm/dịch vụ OA của Zalo * * ĐIỀU KIỆN SỬ DỤNG PURCHASE API: * * 1. QUYỀN TRUY CẬP: * - Ứng dụng cần được cấp quyền quản lý "Mua sản phẩm dịch vụ OA" * - OA phải được xác minh và có quyền bán sản phẩm/dịch vụ * * 2. THỜI GIAN TẠO ĐỚN: * - Order chỉ được tạo từ 00:01 đến 23:54 hàng ngày * - Không thể tạo đơn hàng trong khung giờ bảo trì (23:55 - 00:00) * * 3. SẢN PHẨM/DỊCH VỤ: * - Sản phẩm phải có sẵn và được phép bán * - Đối tượng thụ hưởng (beneficiary) phải phù hợp với sản phẩm * - Có thể sử dụng product_id HOẶC redeem_code, không được dùng cả hai * * 4. THANH TOÁN: * - Tài khoản ZCA phải có đủ số dư để thanh toán * - Voucher code (nếu có) phải hợp lệ và chưa hết hạn * * 5. XÁC THỰC: * - Mỗi đơn hàng sẽ có verified_token (OTT) để xác nhận thanh toán * - OTT có hiệu lực trong vòng 5 phút kể từ khi tạo đơn * * LỖI THƯỜNG GẶP: * - 1001: Beneficiary không hợp lệ * - 1002: Product ID không tồn tại hoặc không khả dụng * - 1003: Redeem code không hợp lệ hoặc đã được sử dụng * - 1004: Voucher code không hợp lệ hoặc đã hết hạn * - 1005: Sản phẩm không khả dụng cho đối tượng thụ hưởng * - 1006: Số dư tài khoản không đủ * - 1007: Ngoài thời gian cho phép tạo đơn hàng * - 1008: Đơn hàng trùng lặp * - 1009: Không có quyền truy cập */ class PurchaseService { constructor(client) { this.client = client; this.purchaseApiUrl = "https://openapi.zalo.me/v3.0/oa/purchase"; } /** * Tạo đơn hàng mua sản phẩm/dịch vụ OA * @param accessToken Access token của Official Account * @param request Thông tin đơn hàng cần tạo * @returns Thông tin đơn hàng đã tạo */ async createOrder(accessToken, request) { try { // Validate request this.validateCreateOrderRequest(request); const result = await this.client.apiPost(`${this.purchaseApiUrl}/create_order`, accessToken, request); if (result.error !== 0) { throw new common_1.ZaloSDKError(result.message || "Failed to create order", result.error, result); } return result.data; } catch (error) { if (error instanceof common_1.ZaloSDKError) { throw error; } throw new common_1.ZaloSDKError(`Failed to create order: ${error.message}`, purchase_1.PurchaseErrorCode.SYSTEM_ERROR, error); } } /** * Tạo đơn hàng với product_id * @param accessToken Access token của Official Account * @param beneficiary Đối tượng thụ hưởng (OA hoặc APP) * @param productId ID sản phẩm * @param voucherCode Mã giảm giá (tùy chọn) * @returns Thông tin đơn hàng đã tạo */ async createOrderWithProduct(accessToken, beneficiary, productId, voucherCode) { const request = { beneficiary, product_id: productId, ...(voucherCode && { voucher_code: voucherCode }), }; return this.createOrder(accessToken, request); } /** * Tạo đơn hàng với redeem_code (mã quà tặng) * @param accessToken Access token của Official Account * @param beneficiary Đối tượng thụ hưởng (OA hoặc APP) * @param redeemCode Mã quà tặng * @param voucherCode Mã giảm giá (tùy chọn) * @returns Thông tin đơn hàng đã tạo */ async createOrderWithRedeemCode(accessToken, beneficiary, redeemCode, voucherCode) { const request = { beneficiary, redeem_code: redeemCode, ...(voucherCode && { voucher_code: voucherCode }), }; return this.createOrder(accessToken, request); } /** * Validate create order request * @param request Request to validate */ validateCreateOrderRequest(request) { // Validate beneficiary if (!request.beneficiary || !["OA", "APP"].includes(request.beneficiary)) { throw new common_1.ZaloSDKError("Beneficiary must be either 'OA' or 'APP'", purchase_1.PurchaseErrorCode.INVALID_BENEFICIARY); } // Validate that either product_id or redeem_code is provided, but not both const hasProductId = (0, purchase_1.isProductOrderRequest)(request); const hasRedeemCode = (0, purchase_1.isRedeemOrderRequest)(request); if (!hasProductId && !hasRedeemCode) { throw new common_1.ZaloSDKError("Either product_id or redeem_code must be provided", purchase_1.PurchaseErrorCode.INVALID_PRODUCT_ID); } if (hasProductId && hasRedeemCode) { throw new common_1.ZaloSDKError("Cannot use both product_id and redeem_code in the same request", purchase_1.PurchaseErrorCode.INVALID_PRODUCT_ID); } // Validate product_id if (hasProductId && (!request.product_id || request.product_id <= 0)) { throw new common_1.ZaloSDKError("Product ID must be a positive number", purchase_1.PurchaseErrorCode.INVALID_PRODUCT_ID); } // Validate redeem_code if (hasRedeemCode && (!request.redeem_code || request.redeem_code.trim() === "")) { throw new common_1.ZaloSDKError("Redeem code cannot be empty", purchase_1.PurchaseErrorCode.INVALID_REDEEM_CODE); } // Validate voucher_code if provided if (request.voucher_code && request.voucher_code.trim() === "") { throw new common_1.ZaloSDKError("Voucher code cannot be empty if provided", purchase_1.PurchaseErrorCode.INVALID_VOUCHER_CODE); } } /** * Kiểm tra thời gian có thể tạo đơn hàng * @returns true nếu trong thời gian cho phép tạo đơn hàng */ isOrderCreationTimeValid() { const now = new Date(); const hours = now.getHours(); const minutes = now.getMinutes(); // Order chỉ được tạo từ 00:01 đến 23:54 if (hours === 0 && minutes === 0) { return false; // 00:00 } if (hours === 23 && minutes >= 55) { return false; // 23:55 - 23:59 } return true; } /** * Tính toán thời gian hết hạn của OTT (One Time Token) * @param createdTime Thời gian tạo đơn hàng (milliseconds) * @returns Thời gian hết hạn OTT (milliseconds) */ calculateOTTExpiration(createdTime) { // OTT có hiệu lực trong vòng 5 phút return createdTime + 5 * 60 * 1000; } /** * Kiểm tra OTT còn hiệu lực hay không * @param createdTime Thời gian tạo đơn hàng (milliseconds) * @returns true nếu OTT còn hiệu lực */ isOTTValid(createdTime) { const now = Date.now(); const expirationTime = this.calculateOTTExpiration(createdTime); return now < expirationTime; } /** * Xác nhận thanh toán đơn hàng * @param accessToken Access token của Official Account * @param request Thông tin xác nhận đơn hàng * @returns Thông tin đơn hàng đã được xác nhận thanh toán */ async confirmOrder(accessToken, request) { try { // Validate request this.validateConfirmOrderRequest(request); const result = await this.client.apiPost(`${this.purchaseApiUrl}/confirm_order`, accessToken, request); if (result.error !== 0) { throw new common_1.ZaloSDKError(result.message || "Failed to confirm order", result.error, result); } return result.data; } catch (error) { if (error instanceof common_1.ZaloSDKError) { throw error; } throw new common_1.ZaloSDKError(`Failed to confirm order: ${error.message}`, purchase_1.PurchaseErrorCode.SYSTEM_ERROR, error); } } /** * Xác nhận thanh toán đơn hàng với order ID và verified token * @param accessToken Access token của Official Account * @param orderId ID đơn hàng * @param verifiedToken OTT token để xác nhận * @returns Thông tin đơn hàng đã được xác nhận thanh toán */ async confirmOrderById(accessToken, orderId, verifiedToken) { const request = { order_id: orderId, verified_token: verifiedToken, }; return this.confirmOrder(accessToken, request); } /** * Lấy thông tin sản phẩm theo ID * @param productId ID sản phẩm * @returns Thông tin sản phẩm hoặc undefined nếu không tìm thấy */ getProductInfo(productId) { return purchase_1.PRODUCT_INFO[productId]; } /** * Lấy danh sách tất cả sản phẩm có sẵn * @returns Danh sách thông tin sản phẩm */ getAllProducts() { return Object.values(purchase_1.PRODUCT_INFO); } /** * Lấy danh sách sản phẩm theo loại * @param category Loại sản phẩm * @returns Danh sách sản phẩm thuộc loại đó */ getProductsByCategory(category) { return Object.values(purchase_1.PRODUCT_INFO).filter(product => product.category === category); } /** * Lấy danh sách sản phẩm theo đối tượng thụ hưởng * @param beneficiary Đối tượng thụ hưởng * @returns Danh sách sản phẩm phù hợp với đối tượng thụ hưởng */ getProductsByBeneficiary(beneficiary) { return Object.values(purchase_1.PRODUCT_INFO).filter(product => product.beneficiary.includes(beneficiary)); } /** * Kiểm tra sản phẩm có phù hợp với đối tượng thụ hưởng không * @param productId ID sản phẩm * @param beneficiary Đối tượng thụ hưởng * @returns true nếu sản phẩm phù hợp với đối tượng thụ hưởng */ isProductCompatibleWithBeneficiary(productId, beneficiary) { const product = this.getProductInfo(productId); if (!product) { return false; } return product.beneficiary.includes(beneficiary); } // ==================== SPECIALIZED PRODUCT APIS ==================== /** * Tạo đơn hàng gói OA Subscription * @param accessToken Access token của Official Account * @param subscriptionType Loại gói subscription * @param voucherCode Mã giảm giá (tùy chọn) * @returns Thông tin đơn hàng đã tạo */ async createOASubscriptionOrder(accessToken, subscriptionType, voucherCode) { const productIdMap = { 'advanced_6m': purchase_1.OA_PRODUCTS.OA_ADVANCED_6M, 'advanced_12m': purchase_1.OA_PRODUCTS.OA_ADVANCED_12M, 'premium_6m': purchase_1.OA_PRODUCTS.OA_PREMIUM_6M, 'premium_12m': purchase_1.OA_PRODUCTS.OA_PREMIUM_12M, }; const productId = productIdMap[subscriptionType]; return this.createOrderWithProduct(accessToken, "OA", productId, voucherCode); } /** * Tạo đơn hàng gói GMF (Group Message Framework) * @param accessToken Access token của Official Account * @param memberLimit Giới hạn số thành viên * @param voucherCode Mã giảm giá (tùy chọn) * @returns Thông tin đơn hàng đã tạo */ async createGMFOrder(accessToken, memberLimit, voucherCode) { const productIdMap = { 10: purchase_1.OA_PRODUCTS.GMF_10_MEMBERS, 50: purchase_1.OA_PRODUCTS.GMF_50_MEMBERS, 100: purchase_1.OA_PRODUCTS.GMF_100_MEMBERS, 1000: purchase_1.OA_PRODUCTS.GMF_1000_MEMBERS, }; const productId = productIdMap[memberLimit]; return this.createOrderWithProduct(accessToken, "OA", productId, voucherCode); } /** * Tạo đơn hàng gói quota tin nhắn giao dịch * @param accessToken Access token của Official Account * @param beneficiary Đối tượng thụ hưởng (OA hoặc APP) * @param quotaSize Kích thước gói quota * @param voucherCode Mã giảm giá (tùy chọn) * @returns Thông tin đơn hàng đã tạo */ async createTransactionQuotaOrder(accessToken, beneficiary, quotaSize, voucherCode) { const productIdMap = { '5k': purchase_1.OA_PRODUCTS.TRANSACTION_5K, '50k': purchase_1.OA_PRODUCTS.TRANSACTION_50K, '500k': purchase_1.OA_PRODUCTS.TRANSACTION_500K, }; const productId = productIdMap[quotaSize]; // Validate beneficiary compatibility if (!this.isProductCompatibleWithBeneficiary(productId, beneficiary)) { throw new common_1.ZaloSDKError(`Product ${quotaSize} transaction quota is not compatible with beneficiary ${beneficiary}`, purchase_1.PurchaseErrorCode.PRODUCT_NOT_AVAILABLE); } return this.createOrderWithProduct(accessToken, beneficiary, productId, voucherCode); } // ==================== BULK ORDER OPERATIONS ==================== /** * Tạo nhiều đơn hàng cùng lúc (batch operation) * @param accessToken Access token của Official Account * @param orders Danh sách đơn hàng cần tạo * @returns Danh sách kết quả tạo đơn hàng */ async createMultipleOrders(accessToken, orders) { const results = []; for (const order of orders) { try { const orderData = await this.createOrder(accessToken, order); results.push({ success: true, data: orderData }); } catch (error) { results.push({ success: false, error: error instanceof common_1.ZaloSDKError ? error.message : error.message }); } } return results; } /** * Tạo combo đơn hàng OA Premium + GMF * @param accessToken Access token của Official Account * @param premiumDuration Thời hạn gói Premium * @param gmfMemberLimit Giới hạn thành viên GMF * @param voucherCode Mã giảm giá (tùy chọn) * @returns Danh sách kết quả tạo đơn hàng */ async createOAPremiumGMFCombo(accessToken, premiumDuration, gmfMemberLimit, voucherCode) { const premiumResult = await this.createOASubscriptionOrder(accessToken, premiumDuration === '6m' ? 'premium_6m' : 'premium_12m', voucherCode).then(data => ({ success: true, data })) .catch(error => ({ success: false, error: error instanceof common_1.ZaloSDKError ? error.message : error.message })); const gmfResult = await this.createGMFOrder(accessToken, gmfMemberLimit, voucherCode).then(data => ({ success: true, data })) .catch(error => ({ success: false, error: error instanceof common_1.ZaloSDKError ? error.message : error.message })); return { premium: premiumResult, gmf: gmfResult }; } // ==================== PRODUCT RECOMMENDATION ==================== /** * Gợi ý sản phẩm dựa trên nhu cầu * @param requirements Yêu cầu của khách hàng * @returns Danh sách sản phẩm được gợi ý */ recommendProducts(requirements) { let recommendations = []; // Filter by beneficiary const compatibleProducts = this.getProductsByBeneficiary(requirements.beneficiary); // Add recommendations based on features if (requirements.features?.includes('subscription')) { const subscriptionProducts = compatibleProducts.filter(p => p.category === 'subscription'); if (requirements.duration === 'long') { recommendations.push(...subscriptionProducts.filter(p => p.name.includes('12 tháng'))); } else { recommendations.push(...subscriptionProducts.filter(p => p.name.includes('6 tháng'))); } } if (requirements.features?.includes('group_messaging')) { const gmfProducts = compatibleProducts.filter(p => p.category === 'gmf'); if (requirements.budget === 'low') { recommendations.push(...gmfProducts.filter(p => p.name.includes('10 thành viên'))); } else if (requirements.budget === 'high') { recommendations.push(...gmfProducts.filter(p => p.name.includes('1000 thành viên'))); } else { recommendations.push(...gmfProducts.filter(p => p.name.includes('50 thành viên') || p.name.includes('100 thành viên'))); } } if (requirements.features?.includes('transaction_quota')) { const quotaProducts = compatibleProducts.filter(p => p.category === 'quota'); if (requirements.budget === 'low') { recommendations.push(...quotaProducts.filter(p => p.name.includes('5k tin'))); } else if (requirements.budget === 'high') { recommendations.push(...quotaProducts.filter(p => p.name.includes('500k tin'))); } else { recommendations.push(...quotaProducts.filter(p => p.name.includes('50k tin'))); } } // Remove duplicates return recommendations.filter((product, index, self) => index === self.findIndex(p => p.id === product.id)); } /** * Validate confirm order request * @param request Request to validate */ validateConfirmOrderRequest(request) { // Validate order_id if (!request.order_id || request.order_id.trim() === "") { throw new common_1.ZaloSDKError("Order ID cannot be empty", purchase_1.PurchaseErrorCode.INVALID_ORDER_ID); } // Validate verified_token if (!request.verified_token || request.verified_token.trim() === "") { throw new common_1.ZaloSDKError("Verified token cannot be empty", purchase_1.PurchaseErrorCode.INVALID_VERIFIED_TOKEN); } } } exports.PurchaseService = PurchaseService; //# sourceMappingURL=purchase.service.js.map