Ứng dụng Tapaya Terminal
Nhận thanh toán thẻ không tiếp xúc bằng cách chuyển phần tương tác với thẻ cho ứng dụng plugin Tapaya Terminal.
Nhận thanh toán thẻ không tiếp xúc từ ứng dụng Android của bạn bằng cách chuyển phần tương tác với thẻ cho ứng dụng plugin Tapaya Terminal.
Ứng dụng của bạn vẫn kiểm soát giao dịch bán hàng; plugin xử lý việc chạm thẻ qua NFC, nhập PIN và xử lý
thẻ, sau đó kết quả được trả về qua luồng sự kiện của Accept.payments.pay().
Hãy dùng cách tích hợp này khi bạn muốn có Tap to Pay mà không cần nhúng một đơn vị xử lý thanh toán vào ứng dụng của mình. Nếu thay vào đó nền tảng của bạn được bật Tap to Pay nhúng, hãy xem hướng dẫn Embedded Tap to Pay.
Các phương án tích hợp
| Phương án | Phù hợp nhất cho | Cách trả kết quả |
|---|---|---|
| Accept SDK (khuyến nghị) | Ứng dụng Android native | Accept.payments.pay() phát ra một Flow<PaymentEvent>: việc tạo, khởi chạy và kết quả cuối cùng đều được xử lý thay bạn |
| Deeplink | WebView, framework đa nền tảng, mã QR | Bạn tạo khoản thanh toán qua Tapaya API và thăm dò (poll) trạng thái của nó; plugin được mở qua một deeplink |
Phương án SDK được trình bày trước; xem Tích hợp không cần SDK để biết quy ước của deeplink.
Cách hoạt động
- Ứng dụng của bạn bắt đầu một khoản thanh toán bằng
Accept.payments.pay(amount, currency). - SDK đăng ký khoản thanh toán với backend Tapaya và khởi chạy plugin Tapaya Terminal trên cùng thiết bị.
- Khách hàng chạm thẻ hoặc điện thoại vào màn hình Tapaya Terminal.
- Tapaya Terminal hoàn tất việc xử lý khoản thanh toán với backend Tapaya.
- SDK theo dõi khoản thanh toán cho tới khi nó đạt trạng thái cuối cùng và phát ra
PaymentEvent.Resultkèm theo mộtPayResult. Nếu hoàn toàn không thể tạo khoản thanh toán, SDK sẽ phát raPaymentEvent.CreationFailedthay vào đó, thường trong vòng một giây.
Điều kiện tiên quyết
- Plugin Tapaya Terminal đã được cài đặt trên thiết bị và đã được kích hoạt cho merchant của bạn (xem Cài đặt và kích hoạt máy thanh toán).
- Thiết bị có NFC, chạy Android 11 (API 30) trở lên.
- Accept SDK đã được cài đặt và khởi tạo, với merchant đã đăng nhập.
- Quyền truy cập vị trí (
ACCESS_FINE_LOCATION) được ứng dụng của bạn cấp khi chạy.
Cài đặt và kích hoạt máy thanh toán
Kích hoạt một lần cho mỗi thiết bị. Việc kích hoạt liên kết plugin Tapaya Terminal với chính tài khoản merchant mà ứng dụng của bạn đang đăng nhập.
if (!Accept.plugin.isInstalled()) {
// Opens the store listing so the user can install Tapaya Terminal
Accept.plugin.install()
return
}
// suspend; launches the plugin's activation flow and waits for the outcome
when (val result = Accept.plugin.activateTerminal()) {
ActivateTerminalResult.Success -> { /* device linked, ready to take payments */ }
ActivateTerminalResult.Canceled -> { /* user backed out */ }
is ActivateTerminalResult.Failed -> result.reason // ActivateTerminalFailureReason
}ActivateTerminalFailureReason là một trong các giá trị INVALID_INTENT, HANDOVER_FAILED, CONFIRM_FAILED,
MERCHANT_NOT_ONBOARDED hoặc UNKNOWN.
Lưu ý
Accept.plugin.isInstalled() trả về false khi thiếu plugin Tapaya Terminal. Bạn có thể đọc trạng thái hiện tại
của plugin bất cứ lúc nào bằng Accept.plugin.status() và điều chỉnh giao diện thanh toán của mình cho phù hợp.
activateTerminal() cũng ném ra NoTidsAvailableForMerchant khi merchant không còn máy thanh toán (TID) nào chưa được gán
để kích hoạt, tách biệt với các kết quả ActivateTerminalResult.Failed ở trên. Hãy bọc lệnh gọi trong
try-catch nếu bạn muốn hiển thị riêng trường hợp này, chẳng hạn bằng cách nhắc merchant giải phóng hoặc
yêu cầu thêm một TID khác.
Kiểm tra bản cập nhật plugin
Accept.plugin.updateInfo() cho biết liệu trên Google Play có bản build mới hơn của plugin Tapaya Terminal hay không,
để bạn có thể nhắc merchant cập nhật trước khi một khoản thanh toán gặp phải chênh lệch phiên bản.
when (val info = Accept.plugin.updateInfo()) {
PluginUpdateInfo.UpToDate -> { /* nothing to do */ }
is PluginUpdateInfo.UpdateAvailable -> promptUpdate(info.priority) // Int, higher is more urgent
PluginUpdateInfo.Unknown -> { /* not installed, or an older plugin build that predates this check */ }
}Lưu ý
SDK không bao giờ truy vấn trực tiếp Google Play; ứng dụng plugin tự kiểm tra qua Play Core và báo kết quả qua
kênh trạng thái hiện có của nó. Bản build plugin cũ hơn bản đã phát hành tính năng này luôn báo
Unknown.
Tạo khoản thanh toán
Số tiền luôn tính theo đơn vị nhỏ nhất của loại tiền tệ: haléř với CZK, cent với EUR. 1500 với
loại tiền tệ "CZK" sẽ trừ 15,00 Kč. Loại tiền tệ dùng mã ISO 4217.
pay() trả về một Flow<PaymentEvent>; hãy collect luồng này để theo dõi tiến trình và kết quả cuối cùng. Hàm này không bao giờ ném ngoại lệ;
lỗi được báo dưới dạng PaymentEvent.CreationFailed.
Accept.payments.pay(amount = 1500, currency = "CZK") // 15.00 Kč
.collect { event ->
when (event) {
PaymentEvent.Creating -> { /* registering the payment */ }
is PaymentEvent.Created -> { /* backend payment created: event.paymentToken */ }
PaymentEvent.Launched -> { /* Tapaya Terminal is in the foreground */ }
is PaymentEvent.Result -> when (val r = event.payResult) {
is PayResult.Success -> showReceipt(r.paymentToken, r.authCode)
is PayResult.Declined -> showDeclined(r.metadata)
is PayResult.Canceled -> showCancelled(r.metadata)
is PayResult.Failed -> showUnexpected(r.reason) // PayFailureReason
}
is PaymentEvent.CreationFailed -> handleError(event.cause) // AcceptException
}
}Tham số:
| Tham số | Kiểu | Bắt buộc | Mô tả |
|---|---|---|---|
amount | Long | ✓ | Số tiền cần trừ, theo đơn vị nhỏ nhất của currency (ví dụ cent với USD, haléř với CZK). |
currency | String | ✓ | Mã tiền tệ ISO 4217. |
paymentToken | String? | Tiếp tục một phiên thanh toán hiện có thay vì bắt đầu phiên mới. Mặc định là null. | |
receiptConfig | PaymentReceiptConfig? | Ghi đè màn hình biên lai hiển thị sau một khoản thanh toán được chấp thuận, chỉ cho lần gọi này. Mặc định là null, khi đó sẽ dùng giá trị mặc định đã đặt bằng Accept.payments.setOptions(). | |
nfcPosition | NfcPositionConfig? | Ghi đè vị trí mà hoạt ảnh tap-to-pay chỉ tới, chỉ cho lần gọi này. Mặc định là null, khi đó sẽ dùng giá trị mặc định đã đặt bằng Accept.payments.setOptions(). | |
metadata | Map<String, String> | Dữ liệu dạng khóa-giá trị gắn vào khoản thanh toán. Được gửi tới plugin Tapaya Terminal trong intent PAY và trả lại trong PayResult tương ứng, nếu plugin trả về. Mặc định là emptyMap(). | |
tip | Long? | Tiền tip cố định, theo đơn vị nhỏ nhất của currency. Khi được đặt, plugin bỏ qua màn hình tip tương tác của riêng nó và trừ tiền amount + tip. Mặc định là null (plugin hỏi người thanh toán về tiền tip). | |
transactionTimeout | Duration? | Ghi đè thời gian máy thanh toán chờ chạm thẻ, chỉ cho lần gọi này. Mặc định là null, khi đó sẽ dùng giá trị mặc định đã đặt bằng Accept.payments.setOptions(). Xem Đặt thời gian chờ giao dịch. | |
display | PaymentDisplay? | Ghi đè màn hình chạy giao diện thanh toán của plugin, chỉ cho lần gọi này. Mặc định là null, khi đó sẽ dùng giá trị mặc định đã đặt bằng Accept.payments.setOptions(). Xem Chọn màn hình hiển thị. |
Trong khi khoản thanh toán đang diễn ra, plugin Tapaya Terminal ở tiền cảnh. Ứng dụng của bạn tự động quay lại
khi khoản thanh toán kết thúc. Để tiếp tục một khoản thanh toán bị gián đoạn, hãy truyền lại paymentToken của nó vào
pay(amount, currency, paymentToken).
metadata không được đảm bảo trả về
metadata chỉ được trả lại trong PayResult nếu plugin trả về, vì vậy hãy coi đây là ngữ cảnh bổ sung không được đảm bảo
(ví dụ mã đơn hàng cho việc ghi log của riêng bạn), chứ không phải dữ liệu mà ứng dụng của bạn bắt buộc phải nhận lại.
Chọn loại tiền tệ
pay() trừ tiền bằng currency mà bạn truyền vào, dưới dạng mã ISO 4217 viết hoa, với amount theo đơn vị nhỏ nhất
của loại tiền tệ đó. Loại tiền tệ được hỗ trợ liệt kê mọi loại tiền tệ mà Tapaya hỗ trợ, nhưng một merchant
thường chỉ được bật một số loại trong đó.
Gọi Accept.merchant.availableCurrencies() để lấy các mã ISO 4217 đã được bật cho merchant. Thanh toán bằng bất kỳ loại tiền tệ
nào khác sẽ thất bại với CurrencyNotAvailableForMerchant.
Mỗi loại tiền tệ cũng có một số tiền thanh toán tối thiểu, được áp dụng trên toàn nền tảng. Lấy các mức tối thiểu hiện tại bằng
Accept.sdk.minimumAmounts(), hàm này trả về một List<MinimumAmount> (currency: String, amount: Long, theo đơn vị nhỏ nhất
của loại tiền tệ). Gọi pay() với số tiền dưới mức tối thiểu đó sẽ thất bại với
PaymentEvent.CreationFailed(AmountBelowMinimum(minimum, currency)). Xem
Xử lý lỗi để biết danh sách ngoại lệ đầy đủ.
Cấu hình màn hình biên lai
receiptConfig kiểm soát những gì plugin Tapaya Terminal hiển thị cho người thanh toán sau một khoản thanh toán được chấp thuận, thời gian
màn hình đó hiển thị và việc có in biên lai hay không. Thiết lập này không ảnh hưởng tới các kết quả bị từ chối, đã đảo giao dịch (reversal) hoặc đang chờ
đảo giao dịch; các kết quả đó luôn hiển thị màn hình kết quả riêng, bất kể thiết lập này.
PaymentReceiptConfig | Mô tả |
|---|---|
DismissImmediately | Không hiển thị gì cho người thanh toán. |
Show(showQrCodeReceipt, dismissBehavior, printBehavior) | Hiển thị màn hình biên lai. showQrCodeReceipt: Boolean = true bật hoặc tắt mã QR liên kết tới biên lai. dismissBehavior: DismissBehavior = DismissBehavior.Manual kiểm soát cách màn hình đóng lại: Manual (giữ nguyên cho tới khi người thanh toán đóng) hoặc Delayed(delay: Duration) (tự động đóng sau delay nếu người thanh toán chưa đóng trước đó). printBehavior: ReceiptPrintBehavior = ReceiptPrintBehavior.OnDemand kiểm soát biên lai giấy: Automatic (in ngay khi màn hình xuất hiện), OnDemand (chỉ in khi thu ngân yêu cầu) hoặc Disabled (không bao giờ in). |
Đặt giá trị mặc định cho toàn thiết bị một lần bằng Accept.payments.setOptions(), và ghi đè cho một khoản thanh toán riêng qua
pay(..., receiptConfig = ...). Cho tới khi setOptions() được gọi, giá trị mặc định là Show với biên lai QR, đóng
thủ công và in theo yêu cầu, trùng với giá trị dự phòng của chính plugin.
// Set a default, e.g. once at app startup
Accept.payments.setOptions(
receiptConfig = PaymentReceiptConfig.Show(
showQrCodeReceipt = true,
dismissBehavior = DismissBehavior.Delayed(30.seconds),
printBehavior = ReceiptPrintBehavior.Automatic,
),
)
// Override it for a single payment
Accept.payments.pay(
amount = 1500,
currency = "CZK",
receiptConfig = PaymentReceiptConfig.DismissImmediately,
)Định vị hoạt ảnh chạm NFC
nfcPosition là một gợi ý cho plugin Tapaya Terminal biết ăng-ten NFC của thiết bị này nằm ở đâu, để plugin có thể đặt
hoạt ảnh “chạm vào đây” cho đúng chỗ. Trước tiên, plugin tự xác định vị trí ăng-ten (phát hiện trực tiếp qua nền tảng,
sau đó là cơ sở dữ liệu thiết bị đi kèm, rồi đến bất cứ thiết lập nào merchant đã cấu hình trực tiếp trong plugin); chỉ cung cấp giá trị này
để ghi đè kết quả xác định đó, chẳng hạn trên một thiết bị mà plugin không phát hiện được hoặc có thông tin sai trong cơ sở dữ liệu.
NfcPositionConfig | Mô tả |
|---|---|
Unspecified | Không ghi đè (mặc định). Plugin tự xác định vị trí và giữ nguyên mọi vị trí mà merchant đã cấu hình ở đó. |
OnDevice(surface, xFraction, yFraction) | Ăng-ten nằm ngay trên thiết bị; hoạt ảnh chỉ tới (xFraction, yFraction) trên surface, là OnDeviceSurface.BACK hoặc OnDeviceSurface.FRONT_SCREEN. |
ExternalAbove | Một mô-đun đầu đọc riêng nằm phía trên điện thoại (giá đỡ smart-POS); không có điểm nào trên thiết bị để chỉ tới. |
Đặt giá trị mặc định cho toàn thiết bị bằng Accept.payments.setOptions(nfcPosition = ...), hoặc ghi đè cho từng lần gọi qua
pay(..., nfcPosition = ...). setOptions() chỉ thay đổi (các) tùy chọn mà bạn truyền vào, nên riêng setOptions(nfcPosition = ...)
sẽ không ảnh hưởng tới giá trị mặc định receiptConfig đã đặt trước đó, và ngược lại.
Đặt thời gian chờ giao dịch
transactionTimeout kiểm soát thời gian plugin Tapaya Terminal chờ khách hàng chạm thẻ trước khi
dừng lại. Tham số nhận bất kỳ Duration nào từ 0 tới 120 giây; mọi giá trị ngoài khoảng đó được báo dưới dạng
TransactionTimeoutOutOfRange(transactionTimeout) qua PaymentEvent.CreationFailed, vì pay() không bao giờ ném ngoại lệ
một cách đồng bộ.
Accept.payments.pay(
amount = 1500,
currency = "CZK",
transactionTimeout = 90.seconds,
)Để nguyên giá trị mặc định null để dùng mức dự phòng của chính ứng dụng đi kèm (khoảng 60 giây), hoặc đặt giá trị mặc định
cho toàn thiết bị bằng Accept.payments.setOptions(transactionTimeout = ...).
Chọn màn hình hiển thị
Trên thiết bị hai mặt (“double-sided”), một màn hình hướng về phía merchant và màn hình thứ hai, dành cho khách hàng, hướng về phía
người thanh toán. Accept.displays cho biết thiết bị hiện có những màn hình nào, và tùy chọn display cho plugin biết
nên chạy giao diện thanh toán trên màn hình nào.
if (Accept.displays.isDualScreen) {
val customerPanel = Accept.displays.customerFacing() // AcceptDisplay?, null on single-screen devices
}
Accept.payments.pay(
amount = 1500,
currency = "CZK",
display = PaymentDisplay.CustomerFacing,
)PaymentDisplay | Mô tả |
|---|---|
Default | Màn hình hiện tại của ứng dụng chủ (hành vi mặc định, không thay đổi trên thiết bị một màn hình). |
CustomerFacing | Màn hình phụ của thiết bị hai mặt. |
Specific(displayId) | Một màn hình cụ thể, theo id được báo trong Accept.displays.all(). |
Một màn hình đích không thể đáp ứng, ví dụ CustomerFacing trên thiết bị một màn hình, sẽ âm thầm chuyển về
Default. Đặt giá trị mặc định cho toàn thiết bị bằng Accept.payments.setOptions(display = ...), hoặc ghi đè cho từng lần gọi qua
pay(..., display = ...).
Đặt màn hình đích trước khi xác thực
Quá trình khởi động trước (warm-up) của máy thanh toán do Accept.auth.authenticate() kích hoạt cũng dùng cùng màn hình đích này, vì vậy hãy gọi
setOptions(display = ...) trước authenticate() nếu bạn muốn chính quá trình warm-up cũng không hiển thị trên
màn hình của merchant.
Ứng dụng mẫu :dualscreen minh họa một quy trình thanh toán đầy đủ trên hai màn hình: merchant thao tác trên màn hình chính, trong khi
PaymentDisplay.CustomerFacing đưa giao diện thanh toán của plugin lên màn hình của khách hàng.
Kết quả thanh toán
Sự kiện cuối cùng PaymentEvent.Result mang theo một PayResult. Mọi biến thể đều mang metadata, trả lại những gì bạn
đã truyền vào pay() nếu plugin trả về (có thể là null):
PayResult | Ý nghĩa |
|---|---|
Success(paymentToken, metadata, authCode, receiptDetails) | Đã được cấp phép và thu tiền (capture). Tiền sẽ được quyết toán cho merchant. authCode là mã cấp phép của đơn vị xử lý do máy thanh toán báo về; chỉ mang tính thông tin, và có thể không có tùy theo đơn vị xử lý. |
Declined(metadata, receiptDetails) | Bị đơn vị xử lý thẻ hoặc ngân hàng phát hành thẻ từ chối. |
Canceled(metadata) | Bị khách hàng hoặc merchant hủy trước khi hoàn tất. |
Failed(reason, metadata) | Không hoàn tất; reason là một PayFailureReason. |
PayFailureReason là một trong các giá trị INVALID_INTENT, NOT_ACTIVATED, TERMINAL_INIT_FAILED, PAYMENT_SETUP_FAILED,
PLUGIN_UNAVAILABLE hoặc UNKNOWN.
Đọc chi tiết biên lai
Success và Declined mang một receiptDetails: ReceiptDetails? tùy chọn, chứa dữ liệu biên lai thẻ có cấu trúc
do máy thanh toán báo về, dành cho bên gọi tự in biên lai thẻ chính thức của mình thay vì chỉ dựa vào
trang do Tapaya lưu trữ tại PaymentStatus.receiptUrl.
| Trường | Kiểu | Mô tả |
|---|---|---|
merchantId | String? | ID merchant do đơn vị xử lý báo về. |
terminalId | String? | ID máy thanh toán do đơn vị xử lý báo về. |
cardBrand | String? | Thương hiệu thẻ, ví dụ Visa hoặc Mastercard. |
appLabel | String? | Nhãn ứng dụng EMV. |
aid | String? | Mã định danh ứng dụng EMV. |
maskedPan | String? | Số thẻ đã được che. |
responseCode | String? | Mã phản hồi của đơn vị xử lý. |
transactionTimestamp | String? | Dấu thời gian giao dịch theo ISO-8601. |
Mọi trường đều là tùy chọn và không được đảm bảo; bên gọi phải sẵn sàng cho trường hợp bất kỳ trường nào, hoặc chính receiptDetails,
không có.
Đọc chi tiết khoản thanh toán
PayResult.Success cung cấp cho bạn paymentToken. Lấy bản ghi đầy đủ, bao gồm receiptUrl, số tiền yêu cầu và
số tiền quyết toán, cùng các dấu thời gian, bằng Accept.payments.status():
val status: PaymentStatus = Accept.payments.status(paymentToken)
status.state // PENDING, SUCCESS, CANCELLED, REFUNDED, FAILED, ACTION_NEEDED
status.receiptUrl // for your receipt UIDừng một khoản thanh toán đang diễn ra bằng Accept.payments.cancel(paymentToken), hàm này trả về PaymentState tương ứng.
Hoàn tiền
Hoàn tiền một khoản thanh toán đã xử lý xong bằng Accept.payments.refund(paymentToken, reason), hàm này cũng trả về
PaymentState tương ứng.
val state: PaymentState = Accept.payments.refund(paymentToken, RefundReason.CUSTOMER_REQUEST)RefundReason là một trong các giá trị CUSTOMER_REQUEST, DUPLICATE, FRAUDULENT hoặc EXPIRED_CHARGE.
Xử lý lỗi
Một khoản thanh toán đã tới máy thanh toán nhưng không thành công không phải là ngoại lệ; nó đến dưới dạng
PaymentEvent.Result mang PayResult.Declined, Canceled hoặc Failed(reason). Các lỗi khiến
khoản thanh toán không thể được tạo sẽ đến dưới dạng PaymentEvent.CreationFailed(cause), trong đó cause là một
AcceptException có kiểu. Các ngoại lệ riêng của cách tích hợp này:
| Ngoại lệ | Khi nào xảy ra | Cần làm gì |
|---|---|---|
PluginUnavailable | Plugin Tapaya Terminal chưa được cài đặt hoặc không thể liên kết (bind) với dịch vụ của nó. | Nhắc cài đặt bằng Accept.plugin.install(). |
CurrencyNotAvailableForMerchant(code) | Merchant chưa được cấu hình cho loại tiền tệ được yêu cầu. | Trừ tiền bằng một loại tiền tệ đã được bật, hoặc cập nhật cấu hình của merchant. |
AmountBelowMinimum(minimum, currency) | Số tiền thấp hơn mức tối thiểu đã cấu hình. | Trừ số tiền bằng hoặc cao hơn minimum. |
TransactionTimeoutOutOfRange(transactionTimeout) | transactionTimeout nằm ngoài khoảng 0 đến 120 giây. | Truyền một giá trị trong khoảng cho phép, hoặc bỏ qua để dùng giá trị mặc định. |
LocationPermissionRequired | Ứng dụng của bạn chưa được cấp quyền truy cập vị trí. | Yêu cầu ACCESS_FINE_LOCATION, sau đó thử lại. |
LocationUnavailable / LocationTimeout | Không thể xác định vị trí của thiết bị. | Yêu cầu người dùng bật định vị và thử lại. |
SessionExpired | Phiên đăng nhập đã hết hạn. | Xác thực lại bằng Accept.auth.authenticate(). |
NoInternetConnection | Không có kết nối mạng. | Yêu cầu người dùng kết nối lại và thử lại. |
PaymentInProgress | Một lệnh gọi pay() trước đó vẫn đang chờ kết quả. | Chờ khoản thanh toán hiện tại kết thúc. |
Accept.payments.status(), cancel() và refund() là các hàm suspend và ném ra các ngoại lệ này
(thay vì phát ra sự kiện); hãy bọc chúng trong try-catch. status()/cancel()/refund() cũng ném ra
PaymentNotFound với token không xác định. Xem danh sách đầy đủ trong
Xử lý lỗi.
Thời gian chờ
Một khoản thanh toán bằng thẻ phải được hoàn tất trên máy thanh toán trong thời gian ngắn sau khi bắt đầu. Nếu khách hàng không hề chạm thẻ, hoặc
plugin Tapaya Terminal bị đóng giữa chừng, khoản thanh toán sẽ kết thúc với PayResult.Canceled hoặc
PayResult.Failed, và khoản thanh toán gốc bị hủy ở phía máy chủ và không bao giờ được xử lý xong. Luôn xác nhận
trạng thái cuối cùng bằng Accept.payments.status() trước khi coi một khoản thanh toán là đã hoàn tất.
Tích hợp không cần SDK
Nếu bạn không thể nhúng Accept SDK (framework đa nền tảng, một POS shell mỏng, máy tính tiền chạy trên web), bạn có thể điều khiển Tapaya Terminal trực tiếp bằng deeplink. Cách phân chia công việc thay đổi: bạn tạo khoản thanh toán qua Tapaya API và bạn thăm dò trạng thái của nó để lấy kết quả; Tapaya Terminal chỉ xử lý phần tương tác với thẻ.
Quy trình:
- Tạo một khoản thanh toán từ backend của bạn qua Payments API và lưu lại
paymentIdcủa nó. - Mở Tapaya Terminal với
paymentIdđó bằng deeplink thanh toán. - Thăm dò trạng thái khoản thanh toán qua Payments API cho tới khi nó rời khỏi
PENDING. Deeplink không có kênh trả kết quả, nên thăm dò là nguồn thông tin đáng tin cậy duy nhất.
Lưu ý
Tapaya Terminal phải được cài đặt, đăng nhập vào tài khoản merchant và được cấu hình cho loại tiền tệ của khoản thanh toán. Chế độ này không có bước kích hoạt; tài khoản đang đăng nhập là bên bán chịu trách nhiệm giao dịch (merchant of record). Nếu cần, hãy đăng nhập cho merchant bằng deeplink đăng nhập.
Scheme của deeplink
Scheme quyết định bản build Tapaya Terminal nào sẽ xử lý liên kết:
| Môi trường | Scheme |
|---|---|
| Production | tapaya-accept |
| Sandbox | tapaya-accept-sandbox |
Nếu một liên kết không mở được, nghĩa là bản build Tapaya Terminal đó chưa được cài đặt.
Deeplink thanh toán
tapaya-accept://pay?paymentId=<paymentId>&amount=<amount>¤cy=<ISO4217>| Tham số truy vấn | Bắt buộc | Ghi chú |
|---|---|---|
paymentId | Có | Khoản thanh toán đã tạo ở bước 1. |
amount | Có | Số tiền theo đơn vị chính dưới dạng số nguyên: amount=15 sẽ trừ 15,00. Lưu ý điều này khác với SDK, vốn nhận đơn vị nhỏ nhất. |
currency | Có | Mã tiền tệ ISO 4217, ví dụ CZK. |
val uri = Uri.parse("tapaya-accept://pay?paymentId=$paymentId&amount=15¤cy=CZK")
try {
startActivity(Intent(Intent.ACTION_VIEW, uri).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
} catch (e: ActivityNotFoundException) {
// Tapaya Terminal is not installed, send the user to Google Play
}Deeplink đăng nhập
Đăng nhập cho merchant vào Tapaya Terminal bằng merchantToken của họ, tức định danh merchant mà bạn đặt qua POST /merchant/auth/register:
tapaya-accept://login?token=<merchantToken>| Tham số truy vấn | Bắt buộc | Ghi chú |
|---|---|---|
token | Có | merchantToken của merchant. Đây là ID merchant ổn định của bạn, không phải token SDK ngắn hạn từ POST /merchant/auth/login. |
activationCode | Không | Điền sẵn mã kích hoạt máy thanh toán. |
Kiểm thử tích hợp
- Khởi tạo với
isProduction = false(mặc định); các khoản thanh toán trong sandbox không bao giờ chuyển tiền thật. - Cài đặt plugin Tapaya Terminal trên thiết bị thử nghiệm, đăng nhập bằng một merchant sandbox và kích hoạt.
- Thực hiện một khoản thanh toán với số tiền nhỏ và kiểm tra toàn bộ luồng
Successtừ đầu đến cuối, bao gồm cả giao diện biên lai của bạn. - Thử các tình huống lỗi: từ chối một khoản thanh toán trên máy thanh toán, hủy một khoản, và bắt đầu một khoản với loại tiền tệ không được hỗ trợ;
phần xử lý
PayResult/CreationFailedcủa bạn cần bao quát từng trường hợp.
Khi bạn sẵn sàng vận hành chính thức, hãy đặt isProduction = true trong initialize và xác thực bằng thông tin đăng nhập
production. Mã thanh toán không thay đổi.