接受使用TAP付款以支付带有条纹的Android
#android #stripe

Stripe最近宣布点击支付Android。让我们研究如何实施此操作,以便您可以使用手机直接开始收取面对面的付款!在这篇博客文章中,让我们假装您在一家名为Coolbeans的(假)食品卡车公司工作,您需要创建一个Android应用程序,以便您可以在旅途中收取付款。

这是我们将要建立的:

GIF of the demo Android application with 3 screens, one to connect to a Terminal, then one to indicate an amount, and finally, the Tap to Pay screen.

本教程使用Kotlin;但是,此功能也可以使用Java实现。如果您需要更多信息,请参阅our docs或查看包含Kotlin和Java中一个示例的Stripe Terminal Android SDK repository

如果您通过视频学得更好,则可以查看the video tutorial on YouTube

先决条件

要与本教程一起,您将需要:

如果您使用的是Android SDK或Kotlin的不同版本,则本教程中的代码样本仍然可以正常工作,但不能保证。

这篇文章只能涵盖在您的Android应用程序中付款所需的代码样本。我将故意不涵盖如何构建和构建Android应用程序,也不会介绍我实施的特定流程的详细信息,因为这与您要构建的内容更相对。

如果您想快速入门,则可以clone my demo application on GitHub,按照读数中的说明进行操作,以确保您将URL更新为后端服务器,并在我解释不同的情况下跟随零件。

克隆和运行演示应用程序后,您应该看到以下屏幕。应用程序的第一部分负责将手机作为读者连接。然后,用户可以输入金额,并最终使用TAP收集付款。

GIF of the demo Android application with 3 screens, one to connect to a Terminal, then one to indicate an amount, and finally, the Tap to Pay screen.

安装条纹终端Android SDK

配置

如果您已经克隆了Tap to Pay demo application,则可以跳过此部分。如果您以前已经与Stripe终端Android SDK集成了

在模块的build.gradle文件中,用最新版本替换条纹终端依赖项,然后添加stripeterminal-localmobile包:

implementation "com.stripe:stripeterminal-localmobile:2.20.0"
implementation "com.stripe:stripeterminal-core:2.20.0"

权限

条纹终端Android SDK需要不同的权限,具体取决于您使用的终端设备的类型。为了付费,需要蓝牙和位置。

为此

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

以及在您的应用程序逻辑中,请检查是否在继续之前授予权限。如果用户授予蓝牙许可,则可以开始初始化终端实例。

初始化终端实例

您需要做的第一件事是初始化一个具有以下代码的终端实例:

Terminal.initTerminal(
  applicationContext, LogLevel.VERBOSE, TokenProvider(),
  TerminalEventListener()
)

这将为给定的应用程序上下文初始化一个终端,您需要将其作为第一个参数传递。然后,您可以将记录的冗长级别传递,这是需要新令牌时使用的连接台面界面的实例,最后,侦听器告知终端生命周期中的事件。

如果您以前曾使用过条纹终端Android SDK,则应该已经为令牌提供商和侦听器创建了接口。如果没有,请随时检查演示应用程序中的TokenProviderTerminalEventListener文件。

加载位置

要使用条纹终端,您需要注册一个或多个位置来管理读者及其活动,通过将它们与物理位置相关联。即使食品卡车可以位于各个地方,但在街上出售食物所需的法规和停车许可证,因此Coolbeans可能会在企业可以运营的地方固定的地点清单。您可以通过the Stripe Dashboard或使用API创建这些位置。

Stripe dashboard showing 2 locations for the fake customer's food trucks

这样,与位置关联的读者的活动将反映在仪表板中,您将能够收集有关不同位置在销售方面的表现的数据。

在您的Android应用程序中,您可以列出您的位置,通过要获取的位置数量的限制以及回调。如果您与演示应用程序一起遵循,则可以在MainActivity.kt file中找到此代码。

Terminal.getInstance().listLocations(
  ListLocationsParameters.Builder().apply {
      limit = 100
  }.build(),
  locationCallback
)

回调将将位置对象保存在可变列表中。

private val mutableListState = MutableStateFlow(LocationListState())

private val locationCallback = object : LocationListCallback {
  override fun onFailure(e: TerminalException) {
      e.printStackTrace()
  }

  override fun onSuccess(locations: List<Location>, hasMore: Boolean) {
      mutableListState.value = mutableListState.value.let {
          it.copy(
              locations = it.locations + locations,
              hasMore = hasMore,
              isLoading = false,
          )
      }
  }
}

找到位置后,您需要编写将标识您将使用的读者的逻辑。

发现读者

要找到可用的读者并选择要使用的读者,您需要调用discoverReaders方法。

实施TAP付款时,配置对象需要将其DiscoveryMethod参数设置为DiscoveryMethod.LOCAL_MOBILE,因此它将运行应用程序运行的手机作为终端设备。

val config = DiscoveryConfiguration(
           timeout = 0,
           discoveryMethod = DiscoveryMethod.LOCAL_MOBILE,
           isSimulated = false,
           location = mutableListState.value.locations[0].id
       )

调用传递此配置对象的discoverReaders方法以及将提供发现读者列表的DiscoveryListener实例。

Terminal.getInstance().discoverReaders(config, discoveryListener = object :
DiscoveryListener {
override fun onUpdateDiscoveredReaders(readers: List<Reader>) {
   // Filtering the list of readers discovered to only store the ones currently online
   readers.filter { it.networkStatus != Reader.NetworkStatus.OFFLINE }
  // For simplicity, I’m only using the first reader retrieved but this code would need to be updated if you wanted to show the list in the UI and let the user select a specific location.
   var reader = readers[0]
   // Handle the connection to the reader in a separate function
   connectToReader(reader)


}, object : Callback {
  override fun onSuccess() {
     println("Finished discovering readers")
  }
  override fun onFailure(e: TerminalException) {
     e.printStackTrace()
  }
})

将读者连接到位置

现在,我们列出了我们的位置并发现了要使用的读者,请使用上述代码示例中创建的connectToReader函数中的connectLocalMobileReader方法将位置连接到读者。

private fun connectToReader(reader: Reader){
    // Pass the location chosen to the LocalMobileConnectionConfiguration method.
       val config = ConnectionConfiguration.LocalMobileConnectionConfiguration("${mutableListState.value.locations[0].id}")
    // Call the connectLocalMobileReader method passing the reader selected, the config object and a callback function.
       Terminal.getInstance().connectLocalMobileReader(
           reader,
           config,
           object: ReaderCallback {
               override fun onFailure(e: TerminalException) {
                   e.printStackTrace()
               }


               override fun onSuccess(reader: Reader) {
                   // [Optional] Update the UI with the location name and terminal ID to indicate to the user that the reader is successfully connected.
                   runOnUiThread {
                       val manager: FragmentManager = supportFragmentManager
                       val fragment: Fragment? = manager.findFragmentByTag(ConnectReaderFragment.TAG)


                       if(reader.id !== null && mutableListState.value.locations[0].displayName !== null){
                           (fragment as ConnectReaderFragment).updateReaderId(
                               mutableListState.value.locations[0].displayName!!, reader.id!!
                           )
                       }
                   }
               }
           }
       )
   }

收集付款

当读者成功连接时,您应该准备好接受与使用其他终端设备时相同的付款方式。您将需要一个带有端点的后端服务器来创建连接令牌并处理用条纹捕获和处理付款时发生的不同事件。如果您已经有一个,则可以克隆我们的Stripe Terminal backend example repo并按照读书中的说明进行设置。

创建付款意图

首先,您需要创建一个PaymentIntent。为此,您的Android应用程序将需要向实现条纹API的服务器提出发布请求。在Demo应用程序中,ApiClient singleton object处理了对后端的电话,并提供了createPaymentIntent方法,您可以致电并传递付款详细信息,包括金额,货币,授权详细信息和回调。
为简单起见,下面的代码示例使用硬编码值,但您需要将应用程序调整到特定的用例中。

ApiClient.createPaymentIntent(
   amount = 100.toLong() ,
   currency = usd,
   extendedAuth = false,
   incrementalAuth = false,
   callback = object : retrofit2.Callback<PaymentIntentCreationResponse> {
       override fun onResponse(
           call: Call<PaymentIntentCreationResponse>,
           response: Response<PaymentIntentCreationResponse>
       ) {
           if (response.isSuccessful && response.body() != null) {
        // Retrieve the payment intent once it is created successfully
               Terminal.getInstance().retrievePaymentIntent(
                   response.body()?.secret!!,
                   createPaymentIntentCallback
               )
           } else {
               println("Request not successful: ${response.body()}")
           }
       }
       override fun onFailure(
           call: Call<PaymentIntentCreationResponse>,
           t: Throwable
       ) {
           t.printStackTrace()
       }
   }
)

成功创建了付款意图时,上面的代码示例调用了retrievePaymentIntent方法,其中包含响应主体中的秘密以检索付款意图。
下面的代码示例显示了如何实现回调函数。

private val createPaymentIntentCallback by lazy {
   object : PaymentIntentCallback {
       override fun onSuccess(paymentIntent: PaymentIntent) {
        collectPaymentMethod(paymentIntent)
       }
       override fun onFailure(e: TerminalException) {
           e.printStackTrace()
       }
   }
}

收集付款方式

创建和检索了付款意图后,我们可以将其传递给collectPaymentMethod方法,并通过回调和小费配置。

private fun collectPaymentMethod(paymentIntent: PaymentIntent){
  // Hardcoded for the purpose of this tutorial
   val skipTipping = true
   val collectConfig = CollectConfiguration.Builder()
       .skipTipping(skipTipping)
       .build()


   Terminal.getInstance().collectPaymentMethod(
       paymentIntent, collectPaymentMethodCallback, collectConfig
   )
}

private val collectPaymentMethodCallback by lazy {
   object : PaymentIntentCallback {
       override fun onSuccess(paymentIntent: PaymentIntent) {
        processPayment(paymentIntent)
       }


       override fun onFailure(e: TerminalException) {
           e.printStackTrace()
       }
   }
}

成功收集付款方式时,我们可以开始处理付款。

处理和捕获付款

要处理付款,您需要调用processPayment方法传递付款意图和一个可以捕获付款的回调功能。

private fun processPayment(paymentIntent: PaymentIntent){
   Terminal.getInstance().processPayment(paymentIntent, processPaymentCallback)
}

private val processPaymentCallback by lazy {
   object : PaymentIntentCallback {
       override fun onSuccess(paymentIntent: PaymentIntent) {
           ApiClient.capturePaymentIntent(paymentIntent.id)
        // Return to previous screen
           navigateTo(PaymentDetails.TAG, PaymentDetails(), true)
       }
       override fun onFailure(e: TerminalException) {
           e.printStackTrace()
       }
   }
}

在这一点上,如果以前的所有步骤都成功,您应该能够运行应用程序,将手机作为读者连接,请参阅调用collectPaymentMethod后的Tap to Pay屏幕,然后点击您的后面的信用卡处理付款的设备。 Coolbeans现在准备接受旅途中的付款!

结论

如果您以前曾使用过Stripe Terminal Android SDK,则只需要进行最小的代码更改即可更新应用程序以实现TAP以付费功能。如果您想快速入门,请随时克隆repository of the demo application并自定义以适应您的用例。如果您想了解更多信息,也可以查看our documentationStripe Terminal Android SDK repository

我们希望您能与我们分享您计划如何使用TAP支付条纹!

您也可以在以下平台上使用Stripe Developer更新保持最新信息:
ð£关注Twitter上的@StripeDevour team
ðiscribe我们的YouTube channel
ð加入官方Discord server
ð§注册Dev Digest

关于作者

Charlie Gerard's profile picture. She is a caucasian woman with long brown hair, wearing glasses. She is standing in front of a white wall with purple lights.

Charlie Gerard是Stripe,published authorcreative technologist的开发人员倡导者。她喜欢研究和experimenting with technologies。当她不编码时,她喜欢在户外度过时光,阅读并设定自己的随机挑战。