安卓 SDK
安装
build.gradle(项目级):
repositories {
maven { url = uri("https://yunopayments.jfrog.io/artifactory/snapshots-libs-release") }
}build.gradle(应用级别):
dependencies {
implementation("com.yuno.sdk:yuno-sdk-android:2.8.1")
}
要求Android 5.0 (API 21) 及以上版本,Kotlin 1.9.24 及以上版本,Java 17,Jetpack Compose(用于 Compose 集成)
Initialize
在应用程序类中:
import com.yuno.sdk.Yuno
import android.app.Application
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
Yuno.initialize(
this,
publicApiKey = "your-public-api-key",
config = YunoConfig()
)
}
}AndroidManifest.xml:
<application
android:name=".MyApplication"
...>
</application>基本支付流程
Jetpack Compose
import androidx.compose.runtime.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.yuno.sdk.Yuno
import com.yuno.sdk.payments.OneTimeToken
@Composable
fun PaymentScreen() {
var checkoutSession by remember { mutableStateOf<String?>(null) }
var paymentMethodSelected by remember { mutableStateOf(false) }
val scope = rememberCoroutineScope()
LaunchedEffect(Unit) {
// 1. Initialize checkout with callback
startCheckout(
callbackPaymentState = { state ->
when (state) {
"SUCCEEDED" -> navigateToSuccess()
"FAIL" -> showError()
else -> {}
}
}
)
// 2. Create session on backend
val session = createCheckoutSession()
checkoutSession = session.checkoutSession
// 3. Update SDK with session and country
updateCheckoutSession(
checkoutSession = session.checkoutSession,
countryCode = "US"
)
}
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
) {
Text("Total: $25.00", style = MaterialTheme.typography.headlineMedium)
Spacer(modifier = Modifier.height(16.dp))
// Payment methods list
Column(
modifier = Modifier
.weight(1f)
.verticalScroll(rememberScrollState())
) {
PaymentMethodListViewComponent(
activity = LocalContext.current as ComponentActivity,
onPaymentSelected = { isSelected ->
paymentMethodSelected = isSelected
}
)
}
// Pay button
Button(
onClick = {
scope.launch {
startPayment(
showStatusYuno = true,
callbackOTT = { token ->
token?.let { createPayment(it, checkoutSession!!) }
}
)
}
},
enabled = paymentMethodSelected,
modifier = Modifier.fillMaxWidth()
) {
Text("Pay Now")
}
}
}
suspend fun createCheckoutSession(): CheckoutSession {
return apiClient.post("/checkout", mapOf(
"amount" to mapOf("currency" to "USD", "value" to 2500),
"customer_id" to "cus_123",
"country" to "US"
))
}
suspend fun createPayment(token: String, checkoutSession: String) {
apiClient.post("/payment/create", mapOf(
"one_time_token" to token,
"checkout_session" to checkoutSession
))
}XML 檢視 (繁體中文)
import com.yuno.sdk.Yuno
import com.yuno.sdk.payments.OneTimeToken
class PaymentActivity : AppCompatActivity() {
private lateinit var checkoutSession: String
private var paymentMethodSelected = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_payment)
initializeCheckout()
findViewById<Button>(R.id.payButton).apply {
isEnabled = false
setOnClickListener {
if (paymentMethodSelected) {
processPayment()
}
}
}
}
private fun initializeCheckout() {
lifecycleScope.launch {
// 1. Initialize checkout with callback
startCheckout(
callbackPaymentState = { state ->
handlePaymentState(state)
}
)
// 2. Create session on backend
val session = createCheckoutSession()
checkoutSession = session.checkoutSession
// 3. Update SDK with session and country
updateCheckoutSession(
checkoutSession = session.checkoutSession,
countryCode = "US"
)
// 4. Set up payment methods view
setupPaymentMethodsView()
}
}
private fun setupPaymentMethodsView() {
// For Compose-based payment methods view:
val composeView = findViewById<ComposeView>(R.id.paymentMethodsContainer)
composeView.setContent {
PaymentMethodListViewComponent(
activity = this@PaymentActivity,
onPaymentSelected = { isSelected ->
paymentMethodSelected = isSelected
findViewById<Button>(R.id.payButton).isEnabled = isSelected
}
)
}
}
private fun processPayment() {
lifecycleScope.launch {
startPayment(
showStatusYuno = true,
callbackOTT = { token ->
token?.let {
createPayment(it, checkoutSession)
}
}
)
}
}
private fun handlePaymentState(state: String?) {
when (state) {
"SUCCEEDED" -> {
Toast.makeText(this, "Payment successful!", Toast.LENGTH_SHORT).show()
startActivity(Intent(this, SuccessActivity::class.java))
finish()
}
"FAIL" -> {
Toast.makeText(this, "Payment failed", Toast.LENGTH_LONG).show()
}
"PROCESSING" -> {
Toast.makeText(this, "Payment is being processed", Toast.LENGTH_SHORT).show()
}
"CANCELED" -> {
Toast.makeText(this, "Payment canceled", Toast.LENGTH_SHORT).show()
}
else -> {}
}
}
private suspend fun createCheckoutSession(): CheckoutSession = withContext(Dispatchers.IO) {
val client = OkHttpClient()
val json = """
{
"amount": {"currency": "USD", "value": 2500},
"customer_id": "cus_123",
"country": "US"
}
""".trimIndent()
val request = Request.Builder()
.url("https://api.example.com/checkout")
.post(json.toRequestBody("application/json".toMediaType()))
.build()
val response = client.newCall(request).execute()
Gson().fromJson(response.body?.string(), CheckoutSession::class.java)
}
private suspend fun createPayment(token: String, checkoutSession: String) = withContext(Dispatchers.IO) {
val client = OkHttpClient()
val json = """
{
"one_time_token": "$token",
"checkout_session": "$checkoutSession"
}
""".trimIndent()
val request = Request.Builder()
.url("https://api.example.com/payment/create")
.post(json.toRequestBody("application/json".toMediaType()))
.build()
client.newCall(request).execute()
}
}处理支付结果
通过处理支付状态变更 callbackPaymentState 回调:
// Set up payment state callback during initialization
startCheckout(
callbackPaymentState = { state ->
when (state) {
"SUCCEEDED" -> {
// Payment successful
navigateToSuccess()
}
"FAIL" -> {
// Payment failed
showError("Payment failed")
}
"PROCESSING" -> {
// Payment is being processed
showPendingMessage()
}
"REJECT" -> {
// Payment was rejected
showError("Payment rejected")
}
"CANCELED" -> {
// User canceled
Toast.makeText(context, "Payment canceled", Toast.LENGTH_SHORT).show()
}
else -> {}
}
}
)
// After startCheckout, update with session and country
updateCheckoutSession(
checkoutSession = session.checkoutSession,
countryCode = "US"
)3DS认证
3DS由SDK自动处理。调用 continuePayment() 在创建付款后,如果 sdk_action_required 是正确的:
startPayment(
showStatusYuno = true,
callbackOTT {token >
lifecycleScope.launch {
val result = createPayment(token,checkoutSession)
if (result.sdkActionRequired) {
continuePayment()
}
}
}
)配置选项
基本参数
| 参数 | 类型 | 说明 |
|---|---|---|
checkoutSession | 字符串 | 后端会话ID |
countryCode | 字符串 | ISO国家代码(例如'US') |
callbackPaymentState | 功能 | 支付状态回调 |
callbackOTT | 功能 | 一次性token |
付款状态状态
const val PAYMENT_STATE_SUCCEEDED = "SUCCEEDED"
const val 支付状态_失败 = "失败"
const val 支付状态_处理中 = "处理中"
const val 支付状态_拒绝 = "拒绝"
const val 支付状态_取消 = "已取消"Proguard规则
proguard-rules.pro:
# Yuno SDK
-keep class com.yuno.sdk.** { *; }
-keep interface com.yuno.sdk.** { *; }
-dontwarn com.yuno.sdk.**
# OkHttp & Retrofit
-dontwarn okhttp3.**
-dontwarn retrofit2.**下一步行动
准备探索更多高级功能了吗?查看《高级功能指南》了解:
- 替代安装方案 -
startPaymentLite()和startPaymentSeamlessLite()用于自定义支付方式选择 - 注册(保存卡片)——保存支付方式以备将来使用
- 拱顶Token ——一键支付,保存卡片
- 自定义用户界面(无头集成)——构建完全定制的支付表单
- 渲染模式集成- 在自定义视图中显示支付表单
- 样式- 使用主题自定义 SDK 外观
- 卡片扫描(OCR)- 启用摄像头扫描卡片功能
- ClearSale集成- 欺诈预防
- 外部浏览器返回(深度链接)——处理支付重定向
另见:
1 天前已更新