阅读此文档前,确保您已完成 集成指南,并了解了 创建收银台的相关 API。
PayKKa 提供的 Android SDK 可以方便在您的 App 内以原生方式嵌入多种支付方式,只需传给 SDK 一个 sessionId 即可在 App 内打开收银台页面,接收用户的付款,并且可以自定义和处理相关的支付回调。SDK 最新的下载地址可以在 Android SDK 历史版本和更新日志 中找到。
SDK 下载后会得到一个 .zip 文件,大致目录结构如下:
.
├── libs
│ ├── alipayplusclient-2.2.0-release.aar
│ ├── cardinalmobilesdk-2.2.7-7.aar
│ ├── paykka-checkout-payments-1.1-Release.aar
│ └── ...
└── PayKKaCheckoutApp-Android
├── JavaCheckoutDemo
│ ├── app
│ ├── build.gradle
│ ├── gradle
│ ├── gradle.properties
│ ├── gradlew
│ ├── gradlew.bat
│ └── settings.gradle
└── KotlinCheckoutDemo
├── app
├── build.gradle.kts
├── gradle
├── gradle.properties
├── gradlew
├── gradlew.bat
└── settings.gradle.kts其中,PayKKaCheckoutApp-Android 包含用 Java 和 Kotlin 语言编写的示例 App 的 Android 项目工程,开发者可以通过示例代码了解 PayKKa SDK 的 API 使用方式。
在 libs 文件夹下的 paykka-checkout-payments-X.X-Release.aar 文件就是 PayKKa 的基础 SDK。将上述 zip 文件中的 libs 文件夹复制到您的 Android 项目中的 app/ 文件夹下,例如 YourAppProject/app/。然后编辑 app/build.gradle.kts 或 app/build.gradle 文件,加入以下代码以引入和使用 SDK。
dependencies {
// 必须,引入libs目录下的所有库和依赖
// paykka-checkout-payments-x.x-Release.aar: 必须,PayKKa的基础SDK
//
// alipayplusclient-x.x.x-release.aar: [Alipay+]必须,Alipay+能力
// cardinalmobilesdk-x.x.x-x.aar: [BankCardPay]必须,用于反欺诈和风控以及相关3DS流程实现
implementation(
fileTree(
mapOf(
"dir" to "libs",
"include" to listOf("*.aar", "*.jar")
)
)
)
// 必须,sdk使用了Gson作为JSON序列化/反序列化库
implementation("com.google.code.gson:gson:2.13.2")
// 必须,sdk的相关UI样式和组件实现基于此库
implementation("com.google.android.material:material:1.12.0")
// 必须,sdk相关接口请求基于okhttp
implementation("com.squareup.okhttp3:okhttp:4.12.0")
// 必须,sdk用到了webkit的一些特性
implementation("androidx.webkit:webkit:1.14.0")
// [GooglePay]必须,Google支付原生SDK
implementation("com.google.android.gms:play-services-wallet:19.5.0")
// [BankCardPay, GooglePay]必须,用于反欺诈和风控以及相关3DS流程实现
implementation("com.stripe:payments-core:22.6.1")
// [WeChatPay]必须,微信支付原生SDK
implementation("com.tencent.mm.opensdk:wechat-sdk-android:6.8.34")
}SDK 在初始化时会校验宿主App的包名及签名证书Hash和PayKKa数据库保存的信息是否一致,以确认SDK调用是来自商户官方渠道打包的App。因此需要告知您的App的以下信息:
- App 包名
- App 签名证书指纹
如您的App已上传Play商店,可以直接前往 测试和发布 -> 应用完整性 -> Play 应用签名 -> 设置 查看证书指纹。
其中,App包名可以在您的App项目中直接找到,App签名的证书指纹可使用 Android App: LibChecker 或者执行以下命令行查看:
# 先导出证书
keytool -exportcert -alias {YourKeyAlias} -keystore {PathToYour}.keystore -rfc > cert.pem
# 用 openssl 算 md5
openssl x509 -in cert.pem -noout -fingerprint -md5OpenSSL 的输出会是如下的一串Hash值:
md5 Fingerprint=7A:3F:9C:10:B4:6D:2E:XX:XX:XX:CC:9A:3D:77:E8:21将您的 App包名 及 签名的证书Hash告诉给我们的对接人员,我们会为您生成 PayKKaAppCode。
之后,在 AndroidManifest.xml 中,为App添加元数据(替换下面的 {YourPayKKaAppCode} 为您的PayKKaAppCode):
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
...
<application ...>
...
<meta-data android:name="paykka_appcode" android:value="{YourPayKKaAppCode}"/>
</application>
</manifest>SDK 引入且配置完后,就可以在您的 App 内使用 PayKKa 的支付SDK了。SDK 主要提供两种方式来使用:Drop-In (嵌入式收银台,完整支付表单弹窗组件,包含多种支付方式) 或者 Component(组件式收银台,仅包含单一支付方式)。
对于每种支付方式,以下均列出了一些 Java/Kotlin 关键代码,通过调用 SDK 的方法,便可以自定义回调并拉起支付表单或弹窗,引导用户完成付款操作。
// ...
open class AppConf : YourMerchantConf() {
// 选填,如果不指定则跟随设备系统设定展示语言
override fun getLocale(): String = "en-US"
}
open class YourMerchantConf : PayKKaConfiguration {
// 必填,填写你的PayKKa商户号
override fun getGatewayMerchantId(): String = "{YOUR_GATEWAY_MERCHANT_ID}"
// 必填,填写你的客户端密钥
override fun getClientKey(): String = "{YOUR_CLIENT_KEY}"
// 选填,你的微信开放平台App Id
override fun getWeChatPayAppId(): String = "{YOUR_WECHAT_PAY_APP_ID}"
// 选填,Your `Participant ID` in Alipay+ Developer Center
override fun getAlipayPlusAcquirerId(): String = "{YOUR_ALIPAY_PARTICIPANT_ID}"
}Drop-In 模式接入的收银台,包含了多种支付方式(例:GooglePay、银行卡/信用卡等支付方式...),接入方式较简单:
在商户后端调用PayKKa初始化Drop-In收银台SessionID的接口后,App端传入SessionID给SDK的支付表单组件,即可拉起包含多种PayKKa支付表单弹窗,引导用户进行付款操作,完成收款流程。

package com.paykka.kotlincheckoutdemo
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.paykka.android.checkout.PayKKa
import com.paykka.android.checkout.PayKKaEnv
import com.paykka.android.checkout.PaymentResult
import com.paykka.android.checkout.PaymentResultCallback
import com.paykka.android.checkout.ui.component.PaymentBottomSheet
import com.paykka.kotlincheckoutdemo.YourMerchantConf.Companion.EXAMPLE_CHECKOUT_SESSION_ID
import com.paykka.kotlincheckoutdemo.ui.theme.KotlinCheckoutDemoTheme
import com.paykka.kotlincheckoutdemo.views.ConfirmOrderScreen
class DropInCheckoutConfirmOrderActivity : ComponentActivity() {
val TAG = "DropInCheckoutConfirmOrderActivity"
private var isPaying by mutableStateOf(false)
// 初始化你的商户配置
private val APP_CONF = AppConf()
private lateinit var paymentSheet: PaymentBottomSheet
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// (1) 必须事先完成对 PayKKa SDK 的初始化
PayKKa.init(this, APP_CONF, PayKKaEnv.SANDBOX)
// (2) 一定要在Activity onCreate 之前完成 初始化 `PaymentBottomSheet` 实例
paymentSheet = PaymentBottomSheet(this, object : PaymentResultCallback {
override fun onResult(paymentResult: PaymentResult) {
// (3) 在 onResult 这个回调中定义你的支付回调,仅会在 EXPIRED/ SUCCESS 后触发此回调
handlePaymentResult(paymentResult)
}
override fun onError(e: Throwable) {
// (4) 在此处可以捕捉到支付流程中的错误
// SDK默认会在UI界面将错误打印出来并友好地提示用户错误原因
// 你可以在这个回调中专注于你的错误处理逻辑
Log.e(TAG, "onError: ", e)
toast(e.message ?: "Unknown error")
hideButtonLoading()
}
})
setContent {
KotlinCheckoutDemoTheme {
Box(modifier = Modifier.fillMaxSize()) {
ConfirmOrderScreen {
openCheckout()
}
if (isPaying) {
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Black.copy(alpha = 0.25f)),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator(modifier = Modifier.size(44.dp))
}
}
}
}
}
}
private fun openCheckout() {
if (isPaying) return
// 可选,是否在表单中展示你的商户名称
paymentSheet.showMerchantName()
showButtonLoading()
// (5) 在恰当时机(例如用户点击“立即支付”按钮时)调用表单弹窗的 init 方法初始化,然后调用 show 方法展示弹窗
paymentSheet
.init(EXAMPLE_CHECKOUT_SESSION_ID)
.show { _ ->
// 表单完全展示完毕后的回调
hideButtonLoading()
}
}
private fun hideCheckout() {
hideButtonLoading()
// 手动关闭表单弹窗
paymentSheet.hide()
}
private fun showButtonLoading() {
isPaying = true
}
private fun hideButtonLoading() {
isPaying = false
}
private fun handlePaymentResult(paymentResult: PaymentResult) {
// 离开页面前先关掉 BottomSheetDialog,避免 WindowLeaked
if (this::paymentSheet.isInitialized && paymentSheet.isShowing) {
paymentSheet.dismiss()
}
val intent = Intent(this, PaymentCompleteActivity::class.java)
intent.putExtra("paymentResult", paymentResult.toString())
startActivity(intent)
finish()
}
override fun onDestroy() {
// 离开页面前先关掉 BottomSheetDialog,避免 WindowLeaked
if (this::paymentSheet.isInitialized && paymentSheet.isShowing) {
paymentSheet.dismiss()
}
super.onDestroy()
}
private fun toast(message: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
}Component 模式,目前PayKKa提供了卡支付组件、Google支付、WeChat支付组件等多种选择,你可以单独地将各类支付方式组件直接嵌入到你的App的任意位置中,可定制化程度较高:
在商户后端调用PayKKa 初始化Component收银台SessionID 的接口后,App端传入SessionID给SDK的单一支付组件,完成收款流程。

- 声明卡支付表单布局
package com.paykka.kotlincheckoutdemo.views
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.paykka.android.checkout.ui.component.card.CardPaymentForm
@Composable
fun BankCardPayConfirmOrderScreen(
isPayButtonEnabled: Boolean,
onPayClick: () -> Unit,
onFormCreated: (CardPaymentForm) -> Unit
) {
Scaffold(
bottomBar = {
Surface(shadowElevation = 8.dp) {
Box(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 12.dp, vertical = 10.dp),
contentAlignment = Alignment.Center
) {
Button(
onClick = onPayClick,
enabled = isPayButtonEnabled,
modifier = Modifier.fillMaxWidth()
) {
Text(text = "立即支付", fontWeight = FontWeight.Bold)
}
}
}
}
) { innerPadding ->
Column(
modifier = Modifier
.padding(innerPadding)
.verticalScroll(rememberScrollState())
.fillMaxSize()
.padding(vertical = 12.dp)
) {
OrderCardView()
AndroidView(
modifier = Modifier
.fillMaxWidth()
.padding(top = 12.dp)
.padding(horizontal = 12.dp),
factory = { context ->
// 引入和声明卡支付表单布局
CardPaymentForm(context).also { onFormCreated(it) }
}
)
}
}
}- 初始化并集成BankCardPaymentSDK
package com.paykka.kotlincheckoutdemo
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.paykka.android.checkout.PayKKa
import com.paykka.android.checkout.PayKKaEnv
import com.paykka.android.checkout.PaymentResult
import com.paykka.android.checkout.PaymentResultCallback
import com.paykka.android.checkout.api.CheckoutApi.CCSInfoResponse
import com.paykka.android.checkout.model.dto.enums.CheckoutStatus
import com.paykka.android.checkout.payments.card.BankCardPayment
import com.paykka.android.checkout.payments.card.BankCardPayment.CardPaymentFormInitCallback
import com.paykka.android.checkout.ui.component.card.CardPaymentForm
import com.paykka.kotlincheckoutdemo.ui.theme.KotlinCheckoutDemoTheme
import com.paykka.kotlincheckoutdemo.views.BankCardPayConfirmOrderScreen
class BankCardPayConfirmOrderActivity : ComponentActivity() {
val TAG = "BankCardPayConfirmOrderActivity"
private var isPaying by mutableStateOf(false)
private var isPayEnabled by mutableStateOf(false)
// 初始化你的商户配置
private val APP_CONF = AppConf()
private lateinit var cardPaymentForm: CardPaymentForm
private lateinit var bankCardPayment: BankCardPayment
private val payCallback = object : PaymentResultCallback {
override fun onResult(paymentResult: PaymentResult) {
// 支付完成的回调,仅会在 EXPIRED/ SUCCESS 后触发此回调
handlePaymentResult(paymentResult)
}
override fun onError(e: Throwable) {
// 在此处可以捕捉到支付流程中的错误
// SDK默认会在UI界面将错误打印出来并友好地提示用户错误原因
// 你可以在这个回调中专注于你的错误处理逻辑
Log.e(TAG, "onError: ", e)
toast(e.message ?: "Unknown error")
hideLoadingOverlay()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// (1) 必须事先完成对 PayKKa SDK 的初始化
PayKKa.init(this, APP_CONF, PayKKaEnv.SANDBOX)
setContent {
KotlinCheckoutDemoTheme {
Box(modifier = Modifier.fillMaxSize()) {
BankCardPayConfirmOrderScreen(
isPayButtonEnabled = isPayEnabled && !isPaying,
onPayClick = { onClickBtnPay() },
onFormCreated = { form ->
cardPaymentForm = form
initCardPaymentForm(form)
initBankCardPayment(form)
}
)
if (isPaying) {
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Black.copy(alpha = 0.25f)),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator(modifier = Modifier.size(44.dp))
}
}
}
}
}
}
private fun initCardPaymentForm(form: CardPaymentForm) {
// (2) 卡组件初始化,并设置表单验证回调
form.init(this).setInputValidationListener(object : CardPaymentForm.InputValidationListener {
override fun onAllFieldsValid() {
// 当所有的表单项填写正确会触发
isPayEnabled = true
Log.i(TAG, "All form fields are valid")
}
override fun onFieldInputInvalid(throwable: Throwable) {
// 当某个表单项填写错误会触发
isPayEnabled = false
Log.e(TAG, "Some form field(s) invalid")
}
})
form.isEnabled = false
}
private fun initBankCardPayment(form: CardPaymentForm) {
// (3) 卡组件的PaymentSDK初始化
// 此阶段会根据收银台配置初始化表单结构和表单项的交互
bankCardPayment = BankCardPayment(this).init(
form,
YourMerchantConf.EXAMPLE_CHECKOUT_SESSION_ID,
object : CardPaymentFormInitCallback {
override fun onComplete(ccsInfoResponse: CCSInfoResponse?) {
// 初始化完成会触发
if (ccsInfoResponse?.data?.status == CheckoutStatus.EXPIRED) {
toast("Expired")
return
}
form.isEnabled = true
}
override fun onFailed(t: Throwable) {
// 初始化失败时会触发
toast(t.message!!)
}
}
)
}
private fun onClickBtnPay() {
if (isPaying) return
if (!this::bankCardPayment.isInitialized || !this::cardPaymentForm.isInitialized) return
showLoadingOverlay()
cardPaymentForm.hideKeyboard()
cardPaymentForm.clearAllInputFocus()
// (4) 发起支付
bankCardPayment.requestPayment(payCallback)
}
private fun showLoadingOverlay() {
isPaying = true
}
private fun hideLoadingOverlay() {
isPaying = false
}
private fun handlePaymentResult(paymentResult: PaymentResult) {
val intent = Intent(this, PaymentCompleteActivity::class.java)
intent.putExtra("paymentResult", paymentResult.toString())
startActivity(intent)
finish()
}
private fun toast(message: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
}
- 声明谷歌支付表单布局
package com.paykka.kotlincheckoutdemo.views
import android.view.ViewGroup
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.paykka.android.checkout.ui.component.googlepay.GooglePaymentForm
@Composable
fun GooglePayConfirmOrderScreen(
googlePaymentForm: GooglePaymentForm
) {
Scaffold(
bottomBar = {
Surface(shadowElevation = 8.dp) {
AndroidView(
modifier = Modifier
.fillMaxWidth()
.padding(12.dp),
// 引入和声明GooglePay表单布局
factory = { googlePaymentForm },
update = { view ->
val parent = view.parent
if (parent is ViewGroup) {
parent.removeView(view)
}
}
)
}
}
) { innerPadding ->
Column(
modifier = Modifier
.padding(innerPadding)
.verticalScroll(rememberScrollState())
.fillMaxSize()
) {
OrderCardView()
}
}
}- 初始化并集成GooglePaymentSDK
package com.paykka.kotlincheckoutdemo
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import com.paykka.android.checkout.PayKKa
import com.paykka.android.checkout.PayKKaConfiguration
import com.paykka.android.checkout.PayKKaEnv
import com.paykka.android.checkout.PaymentResult
import com.paykka.android.checkout.PaymentResultCallback
import com.paykka.android.checkout.payments.PaymentBase
import com.paykka.android.checkout.payments.googlepay.GooglePayment
import com.paykka.android.checkout.ui.component.googlepay.GooglePaymentForm
import com.paykka.android.utils.gson.JsonObject
import com.paykka.kotlincheckoutdemo.YourMerchantConf.Companion.EXAMPLE_CHECKOUT_SESSION_ID
import com.paykka.kotlincheckoutdemo.ui.theme.KotlinCheckoutDemoTheme
import com.paykka.kotlincheckoutdemo.views.GooglePayConfirmOrderScreen
class GooglePayConfirmOrderActivity : ComponentActivity() {
val TAG = "GooglePayConfirmOrderActivity"
private lateinit var googlePayment: GooglePayment
private lateinit var googlePaymentForm: GooglePaymentForm
// 初始化你的商户配置
private val APP_CONF = AppConf()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// (1) 必须事先完成对 PayKKa SDK 的初始化
PayKKa.init(this, APP_CONF, PayKKaEnv.SANDBOX)
// (2) Google Pay初始化
googlePaymentForm = GooglePaymentForm(this)
// 必须在 Lifecycle 进入 STARTED 之前完成初始化 Payment
googlePayment = GooglePayment(this, googlePaymentForm, this::requestPayment)
setContent {
KotlinCheckoutDemoTheme {
GooglePayConfirmOrderScreen(googlePaymentForm = googlePaymentForm)
}
}
}
private fun requestPayment(@Suppress("UNUSED_PARAMETER") view: View) {
googlePaymentForm.showLoadingOverlay()
// (3) 发起支付,拉起谷歌支付弹窗
googlePayment.requestPayment(EXAMPLE_CHECKOUT_SESSION_ID, object : PaymentResultCallback {
override fun onResult(paymentResult: PaymentResult) {
// 支付完成的回调,仅会在 EXPIRED/ SUCCESS 后触发此回调
googlePaymentForm.hideLoadingOverlay()
handlePaymentResult(paymentResult)
}
override fun onError(e: Throwable) {
// 在此处可以捕捉到支付流程中的错误
// SDK默认会在UI界面将错误打印出来并友好地提示用户错误原因
// 你可以在这个回调中专注于你的错误处理逻辑
googlePaymentForm.hideLoadingOverlay()
Log.e(TAG, "onError: ", e)
toast(e.message ?: "Unknown error")
}
override fun onUserCanceled(paymentSource: PaymentBase?, extraData: JsonObject?) {
// 用户取消支付,关闭了谷歌支付弹窗
googlePaymentForm.hideLoadingOverlay()
toast("Google Payment: CANCELED")
}
})
}
private fun handlePaymentResult(paymentResult: PaymentResult) {
val intent = Intent(this, PaymentCompleteActivity::class.java)
intent.putExtra("paymentResult", paymentResult.toString())
startActivity(intent)
finish()
}
private fun toast(message: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
}
- 声明微信支付表单布局(用于显示友好提示信息)
package com.paykka.kotlincheckoutdemo.views
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.paykka.android.checkout.ui.component.wechatpay.WeChatPaymentForm
@Composable
fun WeChatPayConfirmOrderScreen(
isPayButtonEnabled: Boolean,
onPayClick: () -> Unit,
onFormCreated: (WeChatPaymentForm) -> Unit
) {
Scaffold(
bottomBar = {
Surface(shadowElevation = 8.dp) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(top = 10.dp)
) {
AndroidView(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 12.dp),
factory = { context ->
WeChatPaymentForm(context).also { onFormCreated(it) }
}
)
Box(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 12.dp, vertical = 10.dp),
contentAlignment = Alignment.Center
) {
Button(
onClick = onPayClick,
enabled = isPayButtonEnabled,
modifier = Modifier.fillMaxWidth()
) {
Text(text = "微信支付 ¥1118.83", fontWeight = FontWeight.Bold)
}
}
}
}
}
) { innerPadding ->
Column(
modifier = Modifier
.padding(innerPadding)
.verticalScroll(rememberScrollState())
.fillMaxSize()
.padding(vertical = 12.dp)
) {
OrderCardView()
}
}
}- 初始化并集成WeChatPaymentSDK
package com.paykka.kotlincheckoutdemo
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import com.paykka.android.checkout.PayKKa
import com.paykka.android.checkout.PaymentResult
import com.paykka.android.checkout.PaymentResultCallback
import com.paykka.android.checkout.payments.PaymentBase
import com.paykka.android.checkout.payments.wechatpay.WeChatPayment
import com.paykka.android.checkout.ui.component.wechatpay.WeChatPaymentForm
import com.paykka.android.utils.gson.JsonObject
import com.paykka.kotlincheckoutdemo.YourMerchantConf.Companion.EXAMPLE_CHECKOUT_SESSION_ID
import com.paykka.kotlincheckoutdemo.ui.theme.KotlinCheckoutDemoTheme
import com.paykka.kotlincheckoutdemo.views.WeChatPayConfirmOrderScreen
class WeChatPayConfirmOrderActivity : ComponentActivity() {
val TAG = "WeChatPayConfirmOrderActivity"
private var isPaying by mutableStateOf(false)
private var isPayEnabled by mutableStateOf(false)
private lateinit var weChatPaymentForm: WeChatPaymentForm
private lateinit var weChatPayment: WeChatPayment
// 初始化你的商户配置
private val APP_CONF = AppConf()
private val payCallback = object : PaymentResultCallback {
override fun onResult(paymentResult: PaymentResult) {
// 支付完成的回调,仅会在 EXPIRED/ SUCCESS 后触发此回调
hideLoadingOverlay()
handlePaymentResult(paymentResult)
}
override fun onError(e: Throwable) {
// 在此处可以捕捉到支付流程中的错误
// SDK默认会在UI界面将错误打印出来并友好地提示用户错误原因
// 你可以在这个回调中专注于你的错误处理逻辑
Log.e(TAG, "onError: ", e)
hideLoadingOverlay()
toast(e.message ?: "Unknown error")
}
override fun onUserCanceled(paymentSource: PaymentBase?, extraData: JsonObject?) {
// 用户取消支付
hideLoadingOverlay()
toast("WeChat Payment: CANCELED")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// (1) 必须事先完成对 PayKKa SDK 的初始化
PayKKa.init(this, APP_CONF, AppConf.ENVIRONMENT)
setContent {
KotlinCheckoutDemoTheme {
Box(modifier = Modifier.fillMaxSize()) {
WeChatPayConfirmOrderScreen(
isPayButtonEnabled = isPayEnabled && !isPaying,
onPayClick = { onClickBtnPay() },
onFormCreated = { form ->
weChatPaymentForm = form
initWeChatPayment(form)
}
)
if (isPaying) {
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Black.copy(alpha = 0.25f)),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator()
}
}
}
}
}
}
private fun initWeChatPayment(form: WeChatPaymentForm) {
if (this::weChatPayment.isInitialized) return
// (2) WeChat PaymentSDK初始化
weChatPayment = WeChatPayment(this).init(form, EXAMPLE_CHECKOUT_SESSION_ID) {
// 初始化完成的回调
isPayEnabled = true
}
}
private fun onClickBtnPay() {
if (isPaying) return
if (!this::weChatPayment.isInitialized) return
showLoadingOverlay()
// (3) 发起支付
weChatPayment.requestPayment(payCallback)
}
private fun showLoadingOverlay() {
isPaying = true
}
private fun hideLoadingOverlay() {
isPaying = false
}
private fun handlePaymentResult(paymentResult: PaymentResult) {
val intent = Intent(this, PaymentCompleteActivity::class.java)
intent.putExtra("paymentResult", paymentResult.toString())
startActivity(intent)
finish()
}
private fun toast(message: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
}
- 声明Alipay+支付表单布局(用于显示友好提示信息)
package com.paykka.kotlincheckoutdemo.views
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.paykka.android.checkout.ui.component.alipay.AlipayPlusPaymentForm
@Composable
fun AlipayPlusConfirmOrderScreen(
isPayButtonEnabled: Boolean,
onPayClick: () -> Unit,
onFormCreated: (AlipayPlusPaymentForm) -> Unit
) {
Scaffold(
bottomBar = {
Surface(shadowElevation = 8.dp) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(top = 10.dp)
) {
AndroidView(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 12.dp),
factory = { context ->
AlipayPlusPaymentForm(context).also { onFormCreated(it) }
}
)
Box(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 12.dp, vertical = 10.dp),
contentAlignment = Alignment.Center
) {
Button(
onClick = onPayClick,
enabled = isPayButtonEnabled,
modifier = Modifier.fillMaxWidth()
) {
Text(text = "Alipay+支付 ¥1118.83", fontWeight = FontWeight.Bold)
}
}
}
}
}
) { innerPadding ->
Column(
modifier = Modifier
.padding(innerPadding)
.verticalScroll(rememberScrollState())
.fillMaxSize()
.padding(vertical = 12.dp)
) {
OrderCardView()
}
}
}- 初始化并集成AlipayPlusPaymentSDK
package com.paykka.kotlincheckoutdemo
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import com.paykka.android.checkout.PayKKa
import com.paykka.android.checkout.PaymentResult
import com.paykka.android.checkout.PaymentResultCallback
import com.paykka.android.checkout.payments.PaymentBase
import com.paykka.android.checkout.payments.alipay.AlipayPlusPayment
import com.paykka.android.checkout.ui.component.alipay.AlipayPlusPaymentForm
import com.paykka.android.utils.gson.JsonObject
import com.paykka.kotlincheckoutdemo.YourMerchantConf.Companion.EXAMPLE_CHECKOUT_SESSION_ID
import com.paykka.kotlincheckoutdemo.ui.theme.KotlinCheckoutDemoTheme
import com.paykka.kotlincheckoutdemo.views.AlipayPlusConfirmOrderScreen
class AlipayPlusConfirmOrderActivity : ComponentActivity() {
val TAG = "AlipayPlusConfirmOrderActivity"
private var isPaying by mutableStateOf(false)
private var isPayEnabled by mutableStateOf(false)
private lateinit var alipayPlusPaymentForm: AlipayPlusPaymentForm
private lateinit var alipayPlusPayment: AlipayPlusPayment
// 初始化你的商户配置
private val APP_CONF = AppConf()
private val payCallback = object : PaymentResultCallback {
override fun onResult(paymentResult: PaymentResult) {
// 支付完成的回调,仅会在 EXPIRED/ SUCCESS 后触发此回调
hideLoadingOverlay()
handlePaymentResult(paymentResult)
}
override fun onError(e: Throwable) {
// 在此处可以捕捉到支付流程中的错误
// SDK默认会在UI界面将错误打印出来并友好地提示用户错误原因
// 你可以在这个回调中专注于你的错误处理逻辑
Log.e(TAG, "onError: ", e)
hideLoadingOverlay()
toast(e.message ?: "Unknown error")
}
override fun onUserCanceled(paymentSource: PaymentBase?, extraData: JsonObject?) {
// 用户取消支付,关闭了Alipay+支付弹窗
hideLoadingOverlay()
toast("AlipayPlus Payment: CANCELED")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// (1) 必须事先完成对 PayKKa SDK 的初始化
PayKKa.init(this, APP_CONF, AppConf.ENVIRONMENT)
setContent {
KotlinCheckoutDemoTheme {
Box(modifier = Modifier.fillMaxSize()) {
AlipayPlusConfirmOrderScreen(
isPayButtonEnabled = isPayEnabled && !isPaying,
onPayClick = { onClickBtnPay() },
onFormCreated = { form ->
alipayPlusPaymentForm = form
initAlipayPlusPayment(form)
}
)
if (isPaying) {
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Black.copy(alpha = 0.25f)),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator()
}
}
}
}
}
}
private fun initAlipayPlusPayment(form: AlipayPlusPaymentForm) {
if (this::alipayPlusPayment.isInitialized) return
// (2) Alipay+ PaymentSDK初始化
alipayPlusPayment = AlipayPlusPayment(this).init(form, EXAMPLE_CHECKOUT_SESSION_ID) {
isPayEnabled = true
}
}
private fun onClickBtnPay() {
if (isPaying) return
if (!this::alipayPlusPayment.isInitialized) return
showLoadingOverlay()
// (3) 发起支付
alipayPlusPayment.requestPayment(payCallback)
}
private fun showLoadingOverlay() {
isPaying = true
}
private fun hideLoadingOverlay() {
isPaying = false
}
private fun handlePaymentResult(paymentResult: PaymentResult) {
val intent = Intent(this, PaymentCompleteActivity::class.java)
intent.putExtra("paymentResult", paymentResult.toString())
startActivity(intent)
finish()
}
private fun toast(message: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
}您可以在 PayKKa 沙盒环境(SANDBOX)中使用测试卡进行支付测试,在打包 App release 版本时再切换到 PROD 环境。
// 可以在 init 方法中指定使用的支付环境
PayKKa.init(this, APP_CONF, PayKKaEnv.SANDBOX)
/// SANDBOX(默认)
PayKKa.useEnv(PayKKaEnv.SANDBOX)
/// 切换到EU生产环境
PayKKa.useEnv(PayKKaEnv.PROD_EU)
/// 切换到HK生产环境
PayKKa.useEnv(PayKKaEnv.PROD_HK)请参阅:Payment Method - Google Pay。
Google Pay 在测试环境下使用的是测试环境和模拟数据;但上线后会处理真实的用户付款卡信息。为了保护用户和商户的资金与隐私安全,Google 要求:
- 有支持的支付网关(例如 PayKKa)或
- 拥有符合 PCI DSS(支付卡行业数据安全标准)的环境才能申请生产访问权限。这样可以确保您有能力安全处理敏感支付信息,而不是仅在开发测试模式下运行。
因此,您还需要完成 Google Pay - 发布集成 步骤。