Tạo biên nhận PDF sau khi đơn hàng đã được đặt
Trong ví dụ này, chúng ta sẽ đặt một đơn hàng, đọc lại nó với đầy đủ dữ liệu giá cả và lắp ráp một biên nhận có thể in ra dưới dạng PDF trên máy khách.
✅ Mục đích của kịch bản:
- Đặt một đơn hàng và đọc lại nó với Orders.getOrderByMarkerAndId()
- Lắp ráp một biên nhận từ các sản phẩm của đơn hàng, tổng số và phương thức thanh toán
- Hiển thị biên nhận dưới dạng PDF có thể tải xuống trên máy khách
✅ Những gì bạn cần:
- Một PROJECT_URL và APP_TOKEN hợp lệ để xác thực với API OneEntry.
- Người dùng đã đăng ký
- Lưu trữ đơn hàng đã được cấu hình với marker "orders"
📌 Quan trọng:
- Việc hiển thị PDF không phải là một phần của SDK OneEntry — hãy sử dụng bất kỳ thư viện phía máy khách nào (jsPDF được hiển thị ở đây). SDK chỉ cung cấp dữ liệu đơn hàng.
- totalSum được trả về dưới dạng chuỗi và tiền tệ thường trống — định dạng với Number(totalSum).toFixed(2) và không bao giờ mã hóa cứng một ký hiệu tiền tệ.
- Những ví dụ này không bao gồm xử lý lỗi.
- Bạn có thể quản lý lỗi bằng cách sử dụng khối try-catch hoặc bằng cách sử dụng một cấu trúc như await Promise.catch((error) => error).
📚 Xem trong tài liệu:
📦 Tham khảo SDK:
Thử nghiệm trực tiếp
Chạy phương thức này một cách tương tác trong JS SDK sandbox — kết nối URL Dự án và Mã thông báo Ứng dụng của bạn khi truy cập lần đầu, sau đó mở:
- Tạo biên nhận PDF sau khi đơn hàng đã được đặt — Trong ví dụ này, chúng ta sẽ đặt một đơn hàng, đọc lại nó với đầy đủ dữ liệu giá cả và lắp ráp một biên nhận có thể in ra dưới dạng PDF trên máy khách.
Kịch bản
1. Nhập defineOneEntry từ SDK và định nghĩa PROJECT_URL và APP_TOKEN
Ví dụ:
import { defineOneEntry } from 'oneentry';
const PROJECT_URL = 'your-project-url';
const APP_TOKEN = 'your-app-token';
2. Tạo một API client với defineOneEntry()
Ví dụ:
const { AuthProvider, Orders } = defineOneEntry(PROJECT_URL, {
token: APP_TOKEN,
});
3. Xác thực người dùng với AuthProvider.auth()
Ví dụ:
const authData = [
{ marker: 'email_reg', value: 'your-email' },
{ marker: 'password_reg', value: 'your-password' },
];
const user = await AuthProvider.auth('email', { authData });
console.log(user);
Kết quả:
{
"userIdentifier": "kvasssukr.net@gmail.com",
"authProviderIdentifier": "email",
"accessToken": "eyJhbGciOiJIUzI1NiIsInR...pZCI6MTYsImF1dGhQ"
"refreshToken": "1745494429101-...-2834edf8"
}
4. Đặt đơn hàng với Orders.createOrder()
Dữ liệu:
{
"formIdentifier": "order",
"paymentAccountIdentifier": "cash",
"formData": {
"en_US": [
{
"type": "string",
"marker": "name",
"value": "Christina Thomas"
}
]
},
"products": [
{
"productId": 15,
"quantity": 1
}
]
}
Ví dụ:
const body = {
formIdentifier: 'order',
paymentAccountIdentifier: 'cash',
formData: [{ type: 'string', marker: 'name', value: 'Christina Thomas' }],
products: [{ productId: 15, quantity: 1 }],
};
const created = await Orders.createOrder('orders', body as any);
if ('statusCode' in created) {
throw new Error(created.message);
}
Kết quả:
{
"id": 127,
"formIdentifier": "order",
"paymentAccountIdentifier": "cash",
"formData": [
{
"type": "string",
"marker": "name",
"value": "Christina Thomas"
}
],
"products": [
{
"productId": 15,
"quantity": 1
}
],
"currency": "USD",
"totalSum": 400,
"bonusApplied": 0,
"totalDue": 400,
"discountConfig": {
"orderDiscounts": [],
"productDiscounts": [],
"coupon": null,
"settings": {
"allowStacking": false,
"maxDiscountValue": null,
"allowGiftStacking": false,
"maxBonusPaymentPercent": null,
"minBonusAmount": null,
"minOrderAmountForBonus": null,
"giftRefundPolicy": "KEEP_GIFT"
},
"additionalDiscountsMarkers": [],
"totalRaw": 400,
"totalSumWithDiscount": 400,
"excludedGiftProductIds": [],
"bonus": {
"availableBalance": 0,
"maxBonusDiscount": 0,
"minBonusAmount": null,
"minOrderAmountForBonus": null,
"bonusApplied": 0
},
"bonusApplied": 0,
"totalDue": 400
},
"statusIdentifier": "upcoming",
"statusLocalizeInfos": {
"title": "Upcoming"
},
"createdDate": "2026-06-06T12:04:52.459Z"
}
5. Đọc lại đơn hàng đã đặt với Orders.getOrderByMarkerAndId()
Ví dụ:
// This returns the full order: products with titles/prices, totalSum, currency,
// payment method and status — everything the receipt needs.
const order = await Orders.getOrderByMarkerAndId('orders', created.id);
if ('statusCode' in order) {
throw new Error(order.message);
}
Kết quả:
{
"id": 127,
"storageId": 1,
"createdDate": "2026-06-06T12:04:52.459Z",
"statusIdentifier": "upcoming",
"statusLocalizeInfos": {
"title": "Upcoming"
},
"formIdentifier": "order",
"formData": [
{
"type": "string",
"marker": "name",
"value": "Christina Thomas"
}
],
"attributeSetIdentifier": "order",
"paymentStrategy": "once",
"totalSum": "400",
"totalSumRaw": "400",
"currency": "USD",
"paymentAccountIdentifier": "cash",
"paymentAccountLocalizeInfos": {
"title": "Cash"
},
"products": [
{
"id": 15,
"title": "Orange ball",
"sku": null,
"previewImage": "[]",
"price": 400,
"quantity": 1,
"isGift": false
}
],
"paymentUrl": null,
"discountConfig": {
"orderDiscounts": [],
"productDiscounts": [],
"coupon": null,
"settings": {
"allowStacking": false,
"maxDiscountValue": null,
"allowGiftStacking": false,
"maxBonusPaymentPercent": null,
"minBonusAmount": null,
"minOrderAmountForBonus": null,
"giftRefundPolicy": "KEEP_GIFT"
},
"additionalDiscountsMarkers": [],
"totalRaw": 400,
"totalSumWithDiscount": 400,
"excludedGiftProductIds": [],
"bonus": {
"availableBalance": 0,
"maxBonusDiscount": 0,
"minBonusAmount": null,
"minOrderAmountForBonus": null,
"bonusApplied": 0
},
"bonusApplied": 0,
"totalDue": 400
},
"isPartial": false,
"isCompleted": null,
"split": {
"completed": false,
"partial": false,
"stages": [
{
"marker": "default",
"sessionId": null,
"productId": 15,
"title": "Default",
"value": 400,
"status": "planned"
}
]
}
}
6. Lắp ráp biên nhận từ dữ liệu đơn hàng
Ví dụ:
// currency is frequently an empty string — fall back to '' instead of hardcoding '$'.
const currency = order.currency || '';
// Order status is only a marker; its title is project-specific. Prefer the
// localized title from the API, then a client map, then the raw marker.
const STATUS_LABELS: Record<string, string> = {
inProgress: 'In progress',
completed: 'Completed',
};
const receipt = {
orderId: order.id,
// createdDate is an ISO string returned by the API.
date: new Date(order.createdDate).toLocaleString(),
status:
order.statusLocalizeInfos?.title ??
STATUS_LABELS[order.statusIdentifier ?? ''] ??
order.statusIdentifier,
// paymentAccountLocalizeInfos.title is the human label; fall back to the marker.
payment: order.paymentAccountLocalizeInfos?.title || order.paymentAccountIdentifier,
items: order.products.map((p) => ({
title: p.title,
quantity: p.quantity,
price: `${currency}${p.price.toFixed(2)}`,
lineTotal: `${currency}${(p.price * p.quantity).toFixed(2)}`,
})),
// totalSum is a string ("300.00") — convert before formatting.
total: `${currency}${Number(order.totalSum).toFixed(2)}`,
};
console.log(receipt);
Kết quả:
{
"orderId": 127,
"date": "06.06.2026, 15:04:52",
"status": "Upcoming",
"payment": "Cash",
"items": [
{
"title": "Orange ball",
"quantity": 1,
"price": "USD400.00",
"lineTotal": "USD400.00"
}
],
"total": "USD400.00"
}
7. Hiển thị biên nhận dưới dạng PDF trên máy khách (ví dụ: với jsPDF)
Ví dụ:
/*
import { jsPDF } from 'jspdf';
const doc = new jsPDF();
doc.setFontSize(16);
doc.text(`Receipt #${receipt.orderId}`, 14, 20);
doc.setFontSize(11);
doc.text(`Date: ${receipt.date}`, 14, 30);
doc.text(`Status: ${receipt.status}`, 14, 37);
doc.text(`Payment: ${receipt.payment}`, 14, 44);
let y = 58;
receipt.items.forEach((item) => {
doc.text(`${item.quantity} × ${item.title}`, 14, y);
doc.text(item.lineTotal, 196, y, { align: 'right' });
y += 7;
});
doc.setFontSize(13);
doc.text(`Total: ${receipt.total}`, 196, y + 8, { align: 'right' });
doc.save(`receipt-${receipt.orderId}.pdf`);
*/
Ví dụ cuối cùng
// 1. Import defineOneEntry from SDK and define PROJECT_URL and APP_TOKEN
import { defineOneEntry } from 'oneentry';
const PROJECT_URL = 'your-project-url';
const APP_TOKEN = 'your-app-token';
// 2. Creating an API client with [defineOneEntry()](/docs/index/#Installation)
const { AuthProvider, Orders } = defineOneEntry(PROJECT_URL, {
token: APP_TOKEN,
});
// 3. User authorization with [AuthProvider.auth()](/docs/auth-provider/auth)
const authData = [
{ marker: 'email_reg', value: 'your-email' },
{ marker: 'password_reg', value: 'your-password' },
];
const user = await AuthProvider.auth('email', { authData });
console.log(user);
// 4. Place the order with [Orders.createOrder()](/docs/orders/createOrder)
const body = {
formIdentifier: 'order',
paymentAccountIdentifier: 'cash',
formData: [{ type: 'string', marker: 'name', value: 'Christina Thomas' }],
products: [{ productId: 15, quantity: 1 }],
};
const created = await Orders.createOrder('orders', body as any);
if ('statusCode' in created) {
throw new Error(created.message);
}
// 5. Read the placed order back with [Orders.getOrderByMarkerAndId()](/docs/orders/getOrderByMarkerAndId)
// This returns the full order: products with titles/prices, totalSum, currency,
// payment method and status — everything the receipt needs.
const order = await Orders.getOrderByMarkerAndId('orders', created.id);
if ('statusCode' in order) {
throw new Error(order.message);
}
// 6. Assemble the receipt from the order data
// currency is frequently an empty string — fall back to '' instead of hardcoding '$'.
const currency = order.currency || '';
// Order status is only a marker; its title is project-specific. Prefer the
// localized title from the API, then a client map, then the raw marker.
const STATUS_LABELS: Record<string, string> = {
inProgress: 'In progress',
completed: 'Completed',
};
const receipt = {
orderId: order.id,
// createdDate is an ISO string returned by the API.
date: new Date(order.createdDate).toLocaleString(),
status:
order.statusLocalizeInfos?.title ??
STATUS_LABELS[order.statusIdentifier ?? ''] ??
order.statusIdentifier,
// paymentAccountLocalizeInfos.title is the human label; fall back to the marker.
payment: order.paymentAccountLocalizeInfos?.title || order.paymentAccountIdentifier,
items: order.products.map((p) => ({
title: p.title,
quantity: p.quantity,
price: `${currency}${p.price.toFixed(2)}`,
lineTotal: `${currency}${(p.price * p.quantity).toFixed(2)}`,
})),
// totalSum is a string ("300.00") — convert before formatting.
total: `${currency}${Number(order.totalSum).toFixed(2)}`,
};
console.log(receipt);
// 7. Render the receipt as a PDF on the client (e.g. with jsPDF)
/*
import { jsPDF } from 'jspdf';
const doc = new jsPDF();
doc.setFontSize(16);
doc.text(`Receipt #${receipt.orderId}`, 14, 20);
doc.setFontSize(11);
doc.text(`Date: ${receipt.date}`, 14, 30);
doc.text(`Status: ${receipt.status}`, 14, 37);
doc.text(`Payment: ${receipt.payment}`, 14, 44);
let y = 58;
receipt.items.forEach((item) => {
doc.text(`${item.quantity} × ${item.title}`, 14, y);
doc.text(item.lineTotal, 196, y, { align: 'right' });
y += 7;
});
doc.setFontSize(13);
doc.text(`Total: ${receipt.total}`, 196, y + 8, { align: 'right' });
doc.save(`receipt-${receipt.orderId}.pdf`);
*/