Tích hợp Mobile SDK
AndroidTích hợp Tapaya Accept SDK vào ứng dụng di động của bạn.
Tapaya Accept SDK tích hợp việc xử lý thanh toán bằng thẻ trực tiếp vào ứng dụng của bạn. SDK được cung cấp cho Android (Kotlin) và cho React Native / Expo, cả hai đều chạy trên thiết bị Android. Gói React Native là một lớp bọc mỏng bên ngoài Android SDK, nên các bề mặt API, kiểu dữ liệu và trường hợp lỗi bên dưới đều giống nhau trên cả hai.
Mọi mẫu mã trên trang này đều được chia tab theo ngôn ngữ. Chọn một ngôn ngữ và toàn bộ trang sẽ chuyển theo.
Cài đặt
dependencies {
implementation("com.tapaya:accept:1.15.0")
}Android: thêm dependency vào build.gradle.kts ở cấp module, sau đó đồng bộ dự án.
React Native: cài đặt bằng npx expo install @tapayadot/accept-react-native, thêm config plugin như
minh họa, sau đó tạo lại dự án native bằng npx expo prebuild --clean và chạy dự án bằng
npx expo run:android.
Khởi tạo
Accept là điểm truy cập cho mọi chức năng của SDK. Khởi tạo nó một lần khi ứng dụng khởi động (trên Android,
thường là trong lớp Application của bạn), sau đó xác thực riêng bằng token của merchant.
Bước 1: Khởi tạo
// Sandbox is the default (isProduction = false)
Accept.initialize(context = this)
// Target production explicitly
Accept.initialize(context = this, isProduction = true)
// Or drive it from your build type
Accept.initialize(context = this, isProduction = !BuildConfig.DEBUG)Trên React Native không có context; native module giữ nó thay cho bạn.
Bước 2: Xác thực
Gọi Accept.auth.authenticate() sau khi khởi tạo để đăng nhập merchant bằng token lấy từ
backend của bạn. Hàm này là bất đồng bộ và thất bại với một lỗi SDK. Khi thành công, trạng thái SDK chuyển thành authenticated (đã đăng nhập).
// suspend; call from a coroutine
try {
val sdkToken = myBackendApi.getSdkToken()
Accept.auth.authenticate(sdkToken)
// Accept.state now emits SdkState.Authenticated
} catch (e: AcceptException) {
// handle authentication failure
}Theo dõi vòng đời để phản hồi các chuyển đổi trạng thái sau: idle → initialized → authenticated.
// Accept.state is a StateFlow<SdkState>
Accept.state.collect { state ->
if (state == SdkState.Authenticated) loadMerchant()
}Khái niệm cốt lõi
Đối tượng Accept cung cấp SDK thông qua một nhóm nhỏ các bề mặt API:
| Bề mặt API | Mục đích |
|---|---|
Accept.auth | Xác thực phiên của merchant. |
Accept.merchant | Hồ sơ merchant, cấu hình, trạng thái onboarding. |
Accept.payments | Tạo và quản lý các khoản thanh toán bằng thẻ. |
Accept.plugin | Cài đặt và kích hoạt máy thanh toán (ứng dụng plugin). |
Accept.displays | Thông tin màn hình thiết bị cho máy thanh toán hai mặt (“double-sided”). |
Accept.sdk | Phiên bản SDK, môi trường, device id, số tiền tối thiểu. |
Accept.state | Trạng thái vòng đời: StateFlow<SdkState> trên Kotlin, một getter cùng addStateListener() trên React Native. |
Nhận thanh toán
Gọi Accept.payments.pay() với số tiền (theo đơn vị nhỏ nhất của loại tiền tệ) và mã tiền tệ ISO 4217, sau đó
theo dõi luồng các sự kiện PaymentEvent mà nó báo về: đầu tiên là tiến trình, sau đó là kết quả cuối cùng. pay() không bao giờ
ném ngoại lệ; thay vào đó, việc không tạo được khoản thanh toán được báo về dưới dạng sự kiện CreationFailed.
Điểm khác biệt duy nhất là cách bạn theo dõi luồng đó: Kotlin trả về một Flow<PaymentEvent> để bạn collect
từ một coroutine, còn React Native nhận một listener và trả về một subscription mà bạn có thể remove().
amount và currency là bắt buộc. Với các tham số tùy chọn (paymentToken, receiptConfig, nfcPosition,
metadata, tip, transactionTimeout, display), xem Tạo khoản thanh toán.
Trên React Native
Các tham số tương tự được truyền dưới dạng một đối tượng PaymentRequest duy nhất, pay({ amount, currency, tip, … }), với
number thay cho Long và Record<string, string> thay cho Map<String, String>. Có một tham số được đổi tên:
timeout là transactionTimeoutMs và nhận giá trị mili giây thuần (0…120000) thay vì một Duration.

Accept.payments.pay(amount = 15000, currency = "CZK") // 150.00 Kč
.collect { event ->
when (event) {
PaymentEvent.Creating -> Log.d("pay", "creating")
is PaymentEvent.Created -> Log.d("pay", "created ${event.paymentToken}")
PaymentEvent.Launched -> Log.d("pay", "launched")
is PaymentEvent.Result -> Log.d("pay", event.payResult.toString())
is PaymentEvent.CreationFailed -> Log.e("pay", event.cause.toString())
}
}Sự kiện kết quả cuối cùng mang một PayResult: thành công (với paymentToken, metadata, authCode,
receiptDetails), bị từ chối (metadata, receiptDetails), bị hủy (metadata) hoặc thất bại (reason,
metadata). Kotlin mô hình hóa chúng thành các lớp con PayResult.Success / Declined / Canceled / Failed;
React Native mô hình hóa chúng thành một discriminated union mà bạn rẽ nhánh bằng payResult.type === 'success'. Xem
Kết quả thanh toán để biết chi tiết đầy đủ. Truy vấn khoản thanh toán sau đó bằng
Accept.payments.status(paymentToken), dừng nó bằng Accept.payments.cancel(paymentToken), hoặc hoàn tiền bằng
Accept.payments.refund(paymentToken, reason).
Chỉ lấy kết quả (React Native)
Nếu UI của bạn không hiển thị các trạng thái trung gian, payAsync() rút gọn toàn bộ luồng thành PayResult
cuối cùng và reject với một AcceptError cho mọi trường hợp mà pay() lẽ ra đã báo là
creationFailed. Hàm này không có phiên bản tương ứng trên Kotlin; ở đó hãy collect Flow, hoặc chỉ lấy sự kiện cuối cùng của nó.
const result = await Accept.payments.payAsync({ amount: 15000, currency: 'CZK' });
if (result.type === 'success') {
console.log(result.paymentToken, result.receiptUrl);
}Thông tin merchant và SDK
Accept.merchant cung cấp hồ sơ và cấu hình thanh toán của merchant đã đăng nhập. Accept.sdk cung cấp
thông tin về bản build SDK và thiết bị này. Mọi thành phần bên dưới đều yêu cầu gọi Accept.initialize() trước (ngoại trừ
Accept.sdk.version), và mọi thành phần của Accept.merchant còn yêu cầu thêm Accept.auth.authenticate()
đã hoàn tất, nếu không lời gọi sẽ ném NotInitialized hoặc NotAuthenticated.
Merchant
val info = Accept.merchant.info() // MerchantInformation?
val config = Accept.merchant.config() // MerchantConfig
val currencies = Accept.merchant.availableCurrencies() // List<String>
val status = Accept.merchant.onboardingStatus() // OnboardingStatus| Thành phần | Trả về | Mô tả |
|---|---|---|
info() | MerchantInformation? | Hồ sơ doanh nghiệp đã đăng ký của merchant. null nếu hồ sơ chưa có sẵn. |
config() | MerchantConfig | Cấu hình thanh toán của merchant. |
availableCurrencies() | List<String> | Các mã ISO 4217 mà merchant có thể chấp nhận. Dạng rút gọn của config().availableCurrencies. |
onboardingStatus() | OnboardingStatus | Merchant đã được phép nhận thanh toán hay chưa. |
Cả bốn đều là hàm suspend và ném NotAuthenticated, NoInternetConnection hoặc Unknown khi thất bại.
MerchantInformation chứa hồ sơ doanh nghiệp đã đăng ký. Luôn có: merchantId (UUID),
email, businessType (BusinessType), legalName, registrationNumber, mcc và địa chỉ đã đăng ký
(addressLine1, city, country). Tùy chọn (null trừ khi merchant đã cung cấp): vatNumber,
incorporationDate, addressLine2, postalCode, state, phone, averageItemValue,
expectedMonthlyTurnover, businessModelDescription, numberOfEmployees, annualTurnover,
balanceSheetTotal, statementDescriptor, termsVersion.
BusinessType là một trong các giá trị INDIVIDUAL, COMPANY, NONPROFIT_ORGANIZATION, GOVERNMENT_ENTITY hoặc
PARTNERSHIP.
MerchantConfig chứa availableCurrencies: List<String> và currency: String? mặc định của merchant.
OnboardingStatus là một sealed interface: hoặc là ReadyForPayments, hoặc là RequiresAction(state), trong đó
state là một OnboardingState: NOT_STARTED, PENDING, ACTIVE, ATTENTION_NEEDED, ACTION_REQUIRED,
BLOCKED, WAITING_FOR_KYB, WAITING_FOR_STAKEHOLDERS hoặc PENDING_ACQUIRER_APPROVAL. Xem
Onboarding merchant để biết ý nghĩa của từng trạng thái.
SDK
Accept.sdk.version // String, e.g. "1.15.0"
Accept.sdk.isProduction // Boolean
Accept.sdk.deviceId() // String? (suspend)
Accept.sdk.minimumAmounts() // List<MinimumAmount> (suspend)| Thành phần | Trả về | Mô tả |
|---|---|---|
version | String | Phiên bản của SDK. Có thể đọc trước khi gọi initialize(). |
isProduction | Boolean | initialize() đã nhắm đến production (true) hay sandbox (false). |
deviceId() | String? | Id cố định của thiết bị này, được gán ở lần authenticate() thành công đầu tiên và được giữ cho đến khi gỡ cài đặt ứng dụng. null trước thời điểm đó. Hãy cung cấp id này khi gửi yêu cầu hỗ trợ. |
minimumAmounts() | List<MinimumAmount> | Số tiền thanh toán tối thiểu cho từng loại tiền tệ được hỗ trợ. Mỗi mục là currency: String, amount: Long theo đơn vị nhỏ nhất của loại tiền tệ. |
Ứng dụng plugin
Mọi khoản thanh toán bằng thẻ đều được chuyển cho Tapaya Terminal, ứng dụng plugin của Tapaya; ứng dụng này luôn bắt buộc, không có luồng thanh toán bằng thẻ nào bỏ qua nó. Kiểm tra xem ứng dụng đã được cài đặt chưa, sau đó kích hoạt máy thanh toán, hoặc mở trang ứng dụng trên cửa hàng để merchant có thể cài đặt.
Dùng Accept.plugin.isInstalled(), Accept.plugin.activateTerminal() và Accept.plugin.install() cho việc này. Xem
Cài đặt và kích hoạt máy thanh toán để biết mã và mọi
kết quả kích hoạt.
Đọc trạng thái hiện tại của plugin bằng Accept.plugin.status(), hoặc đăng xuất merchant khỏi plugin bằng
Accept.plugin.logout().
Quyền truy cập
Thanh toán bằng thẻ yêu cầu ứng dụng chủ có quyền truy cập vị trí (ACCESS_FINE_LOCATION). SDK không
yêu cầu quyền này thay bạn; hãy cấp quyền bằng các API quyền tiêu chuẩn của Android trước khi gọi pay().
class PaymentActivity : AppCompatActivity() {
private val requestLocation = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { granted ->
if (granted) launchPaymentFlow() else showPermissionRationale()
}
fun startPayment() {
requestLocation.launch(Manifest.permission.ACCESS_FINE_LOCATION)
}
}Nếu thiếu quyền vị trí hoặc không xác định được vị trí khi pay() chạy, flow sẽ phát ra PaymentEvent.CreationFailed
với nguyên nhân là LocationPermissionRequired, LocationUnavailable hoặc LocationTimeout.
Xử lý lỗi
Mọi lỗi được kiểm soát đều mang một trong các tên dưới đây. Trên Kotlin, mỗi lỗi là một lớp con của sealed class
AcceptException; trên React Native, tất cả đều là một lớp AcceptError duy nhất có code chứa tên lỗi, còn các
trường payload (minimum, currency, state, …) nằm ngay trên cùng đối tượng đó.
Các lời gọi bất đồng bộ (authenticate, merchant.*, payments.status/cancel/refund,
plugin.activateTerminal/status/logout/updateInfo, clear) thất bại với một trong các lỗi này. payments.pay() là
ngoại lệ: hàm này không bao giờ ném lỗi, và báo các lỗi tương tự dưới dạng sự kiện CreationFailed mang theo nguyên nhân.
Các loại exception
| Exception | Mô tả |
|---|---|
| Vòng đời | |
NotInitialized | Lời gọi cần Accept.initialize() được gọi trước. |
NotAuthenticated | Lời gọi cần Accept.auth.authenticate() được gọi trước. |
SessionExpired | Phiên đăng nhập đã hết hạn; hãy xác thực lại. |
UnknownEnvironment(key) | Không nhận diện được khóa môi trường (overload initialize nội bộ). |
| Thanh toán | |
PaymentInProgress | Một lời gọi pay() trước đó vẫn đang chờ kết quả. |
CurrencyNotAvailableForMerchant(code) | Loại tiền tệ chưa được cấu hình cho merchant. |
UnsupportedCurrency(code) | SDK không nhận diện được loại tiền tệ. |
AmountBelowMinimum(minimum, currency) | Số tiền thấp hơn mức tối thiểu đã cấu hình. |
TransactionTimeoutOutOfRange(transactionTimeout) | transactionTimeout nằm ngoài khoảng từ 0 đến 120 giây. |
PaymentCreationFailed(cause) | Mọi lỗi khác khi tạo khoản thanh toán. |
PaymentNotFound | Backend không nhận ra token truyền cho status()/cancel()/refund(). |
| Vị trí | |
LocationPermissionRequired | Ứng dụng chủ không có quyền truy cập vị trí. |
LocationUnavailable | Không có provider nào xác định được vị trí. |
LocationTimeout | Không xác định được vị trí kịp thời. |
| Plugin / Máy thanh toán | |
PluginUnavailable | Ứng dụng plugin chưa được cài đặt, hoặc không thể bind tới service của nó. |
PluginTimeout | Plugin không phản hồi kịp thời. |
ActivationInProgress | Một lời gọi activateTerminal() trước đó vẫn đang chờ kết quả. |
NoTidsAvailableForMerchant | Merchant không còn máy thanh toán (TID) nào chưa được gán để kích hoạt. |
PluginAlreadyAuthenticated | Plugin đã đăng nhập cho merchant này. |
PluginMerchantMismatch | Plugin đã đăng nhập dưới một merchant khác; hãy gọi plugin.logout() trước. |
PluginNotReadyForPayments(status) | pay() chạy trong khi plugin không ở trạng thái PluginStatus.ReadyForPayments; status chứa trạng thái hiện tại. |
MerchantOnboardingIncomplete(state) | Merchant chưa hoàn tất onboarding. |
| Hệ thống | |
NoInternetConnection | Thiết bị không có kết nối mạng. |
Unknown(cause) | Mọi lỗi mà SDK không nhận diện là lỗi của chính nó. |
Kết quả và exception
Một khoản thanh toán đã đến được máy thanh toán nhưng không thành công không phải là exception; nó được trả về dưới dạng
PaymentEvent.Result mang PayResult.Declined, Canceled hoặc Failed(reason). Tương tự, việc kích hoạt máy thanh toán
báo ActivateTerminalResult.Failed(reason).
Xử lý exception
Bọc lời gọi trong try/catch và rẽ nhánh theo các trường hợp cụ thể mà bạn xử lý, với một nhánh catch tổng quát làm phương án dự phòng.
Kotlin rẽ nhánh theo lớp con exception; React Native kiểm tra bằng AcceptError.is() và dùng switch trên code.
try {
val status = Accept.payments.status(paymentToken)
} catch (e: SessionExpired) {
// Re-authenticate
} catch (e: NoInternetConnection) {
// Ask the user to check connectivity
} catch (e: AcceptException) {
// Handle any other SDK error
Log.e("Payment", "Error: ${e.message}")
}Ghi log
Bật log gỡ lỗi trong quá trình phát triển để khắc phục sự cố.
Accept.setDebugLoggingEnabled(true)Đăng xuất
Xóa toàn bộ trạng thái SDK được lưu cục bộ (auth token, device id, cấu hình đã lưu đệm) và đặt lại trạng thái SDK về
idle. Gọi hàm này khi người dùng đăng xuất. Để đồng thời đăng xuất merchant khỏi ứng dụng Tapaya Terminal, hãy gọi
Accept.plugin.logout() trước.
Accept.plugin.logout() // suspend; signs out of the Tapaya Terminal app
Accept.clear() // suspend; clears local SDK state