Headless SDK (Payment)

Yuno's Headless SDK lets you create payments and enroll payment methods simultaneously. Note that when using the Headless SDK, you will need to request and send via API all the mandatory fields the payment provider requires to generate payment in their API. Yuno recommends using one of the other SDK implementations, such as Full or Lite, for more frictionless integration.

Yuno's Headless SDK enables you to create payments in two different scenarios:

  • Create a One-Time Use Token using credit card information or alternative payment methods, then create a payment.
  • Create a One-Time Use Token using a vaulted_token from a previously enrolled payment method to collect relevant information for fraud providers, then create a payment.

The following steps describe creating a payment using Yuno's Headless SDK.

Step 1: Include the library in your project.

The first step is to include Yuno SDK in your project through Gradle.

Android SDK Versions

To check all versions available, you can access the release page from the Yuno Android SDK repository.

Then, you can add the repository source, as shown in the following code block:

maven { url "https://yunopayments.jfrog.io/artifactory/snapshots-libs-release" }

Next, you need to add the Yuno SDK dependency to your application. To accomplish it, add the code below in the build.gradle file.

dependencies {
    implementation 'com.yuno.payments:android-sdk:{last_version}'
}

Permissions

Yuno SDK includes, by default, the INTERNET permission, which is required to make network requests.

<uses-permission android:name="android.permission.INTERNET" />

Step 2: Initialize Headless SDK with the public key

To initialize the Headless SDK, you need to import Yuno and provide a valid PUBLIC_API_KEY. If you don't have your API credentials, access the Developers (Credentials) page to check how to retrieve them from the dashboard.

If you haven't implemented a custom application, create one. Then, in the onCreate() method of your application class, call the Yuno.initialize() function to initialize the SDK. The code block below presents an example of using Yuno.initialize().

class CustomApplication : Application() {
  override fun onCreate() {
    super.onCreate()
    Yuno.initialize(
      this,
      "your api key",
      config: YunoConfig, // This is a data class to use custom configs in the SDK.
    )
  }
}

Step 3: Start the checkout process

Next, you will start the checkout process using the apiClientPayment function, providing the necessary configuration parameters. You need to call this function once your customer selects the payment method. As a result, the SDK will start collecting relevant information for 3DS and fraud prevention tools you have configured in your routing.

The following table lists all required parameters and their descriptions.

ParameterDescription
country_codeThis parameter determines the country for which the payment process is being configured. The complete list of supported countries and their country_code is available on the Country coverage page.
checkout_sessionRefers to the current payment's checkout session created using the Create Checkout Session endpoint.
Example: '438413b7-4921-41e4-b8f3-28a5a0141638'

The following code block presents an example of the parameter configuration.

 val apiClientPayment = Yuno.apiClientPayment(
   // country can be one of the following: https://docs.y.uno/docs/country-coverage-yuno-sdk
   countryCode = "CO", -> country can be one of the following: https://docs.y.uno/docs/country-coverage-yuno-sdk
   // The customer_session created in https://docs.y.uno/reference/create-customer-session
   checkoutSession = "74bf4b96-6b35-42a6-8c73-2fe094c34ca9", -> The checkout_session created using the following endpoint https://docs.y.uno/reference/create-checkout-session
   context = this -> This is the context of your activity
)

Step 4: Generate token

After collecting all user information, you can start the payment. First, you need to create an OTT using the function apiClientPayment.generateToken. As it is an asynchronous function, you can use try/catch to ensure you will correctly handle triggered errors. Below, you will find two examples of different scenarios to create an OTT:

  1. Example 1: Create an OTT utilizing a card as the payment method and including all requisite card information.
  2. Example 2: Create an OTT using the vaulted_token information.

Benefits of using a vaulted token

When you use a vaulted token with the SDK, all the fraud information from the providers you configured in your card routing is collected and attached to the OTT. In addition, you can add installment information and a security code if the provider requires it.

/**
 * Create One Time Use Token
 * This will trigger an error if there is missing data
 * You can catch it using a try/catch
 */

apiClientPayment.generateToken(
   collectedData = TokenCollectedData(
     	 // The checkout_session created using the following endpoint https://docs.y.uno/reference/create-checkout-session
       checkoutSession = "checkout_session",
     	 // The necessary info to use the payment method structure
       paymentMethod = PaymentMethod (
           type = "CARD",
         	 // Send this value if you already have a registered or enrolled payment method.
     			 // Other fields like card and customer are optional unless your provider requires them.
           vaultedToken = null,
           card = CardData(
             	 // Set this value to “true” if you want to generate a vaulted_token (tokenize) the card.
               save = true,
               detail = Detail(
                   expirationMonth = 11,
                   expirationYear = 55,
                   number = "4111111111111111",
                   securityCode = "123",
                   holderName = "Firstname Lastname",
                   type = CardType.DEBIT
               ),
               // Only necessary if an installment plan is created for the account.
               installment = Installment(
                   id = "id",
                   value = 12
               )
           ),
           customer = Customer(
             	 // You can check the object here: https://docs.y.uno/reference/the-customer-object
      				 // You create the customer using the following endpoint: https://docs.y.uno/reference/create-customer
               id = "id",
               merchantCustomerId = "merchant_customer_id",
               firstName = "firstName",
               lastName = "lastName",
               gender = Gender.NB,
               dateOfBirth = "DD/MM/YYYY",
               email = "[email protected]",
               country = "CO",
               document = Document(
                   documentType = "PAS",
                   documentNumber = "PAS12312"
               ),
               phone = Phone(
                   number = "321123321123",
                   countryCode = "57"
               )
           )
       )
   ),
   context = this
)

/**
 * Create One Time Use Token
 * This will trigger an error if there is missing data
 * You can catch it using a try/catch
 */

apiClientPayment.generateToken(
   collectedData = TokenCollectedData(
			 // The checkout_session created using the following endpoint https://docs.y.uno/reference/create-checkout-session
       checkoutSession = "checkout_session",
     	 // The necessary info to use the payment method structure     
       paymentMethod = PaymentMethod(
           type = "CARD",
           vaultedToken = "a1c7c5d1-b260-4dc6-909a-8368704233cf",
           card = CardData(
               save = true,
               detail = Detail(
                   expirationMonth = 11,
                   expirationYear = 55,
                   number = "4111111111111111",
                   securityCode = "123",
                   holderName = "Firstname Lastname",
                   type = CardType.DEBIT
               ),
               // Only necessary if an installment plan is created for the account.             
               installment = Installment(
                   id = "id",
                   value = 12
               )
           ),
           customer = Customer(
             	 // You can check the object here: https://docs.y.uno/reference/the-customer-object
      				 // You create the customer using the following endpoint: https://docs.y.uno/reference/create-customer             
               id = "id",
               merchantCustomerId = "merchant_customer_id",
               firstName = "firstName",
               lastName = "lastName",
               lastName = "lastName",
               gender = Gender.NB,
               dateOfBirth = "DD/MM/YYYY",
               email = "[email protected]",
               country = "CO",
               document = Document(
                   documentType = "PAS",
                   documentNumber = "PAS12312"
               ),
               phone = Phone(
                   number = "321123321123",
                   countryCode = "57"
               )
           )
       )
   ),
   context = this
)

PCI Compliance

Please bear in mind that you are capturing sensitive card data. Therefore, you need to comply with good practices regarding data management. If you don't have a PCI certification, you can't save any card data other than the token provided by the SDK.

The apiClientPayment.generateToken function returns an Observable type, which is a subclass of LiveData. As a result, you can observe the response as a common LiveData with the following type SingleLiveEvent<Map<String, Any?>>, which is a LiveData that only emits once. The response type is a Map containing the whole response. The following code block presents the examples of response after calling the apiClientPayment.generateToken function.

["token": "9ee44ac7-9134-4598-ae28-a26fec03099d",
     "type": "CARD",
     "customer": ["billing_address": null,
                  "first_name": null,
                  "gender": "",
                  "phone": nil,
                  "browser_info": ["color_depth": null,
                                   "language": "en",
                                   "accept_header": "*/*",
                                   "browser_time_difference": null,
                                   "accept_content": null,
                                   "accept_browser": null,
                                   "java_enabled": null,
                                   "user_agent": "YunoSDK_Example/1 CFNetwork/1406.0.4 Darwin/22.6.0",
                                   "screen_height": "844.0",
                                   "screen_width": "390.0",
                                   "javascript_enabled": null],
                  "document": null,
                  "last_name": null,
                  "device_fingerprint":null,
                  "email": null],
     "country": "BR",
     "vaulted_token": null,
     "installment": ["rate": "",
                     "id": "cca80084-961b-4212-9c34-54f03f4f10ae",
                     "value": 24,
                     "amount": null],
     "card_data": null]

["token": "9ee44ac7-9134-4598-ae28-a26fec03099d",
     "type": "CARD",
     "customer": ["billing_address": null,
                  "first_name": null,
                  "gender": "",
                  "phone": nil,
                  "browser_info": ["color_depth": null,
                                   "language": "en",
                                   "accept_header": "*/*",
                                   "browser_time_difference": null,
                                   "accept_content": null,
                                   "accept_browser": null,
                                   "java_enabled": null,
                                   "user_agent": "YunoSDK_Example/1 CFNetwork/1406.0.4 Darwin/22.6.0",
                                   "screen_height": "844.0",
                                   "screen_width": "390.0",
                                   "javascript_enabled": null],
                  "document": null,
                  "last_name": null,
                  "device_fingerprint":null,
                  "email": null],
     "country": "BR",
     "vaulted_token":"a1c7c5d1-b260-4dc6-909a-8368704233cf",
     "installment": ["rate": "",
                     "id": "cca80084-961b-4212-9c34-54f03f4f10ae",
                     "value": 24,
                     "amount": null],
     "card_data": null]

PCI Compliance

The endpoint response provide the that defines if additional actions are necessary

The code block below presents an example of observing the response.

apiClientPayment.generateToken(data, context).observe(context) { response ->
   val token = response["token"] as String?
   val error = response["error"] as String?
}

Step 5: Create the Payment

After receiving the OTT, you can create the payment using the Create Payment endpoint to get the final payment result. You will inform the OTT received in Step 4 through the request's payment_method.token parameter. The following code block presents a request example for creating a payment.

{
  "merchant_order_id": "0000022",
  "country": "CO",
  "account_id": "<Your account_id>",
  "description": "Test",
  "amount": {
    "currency": "COP",
    "value": 52000
  },
  "customer_payer": {
    "id": "cfae0941-7234-427a-a739-ef4fce966c79"
  },
  "checkout": {
    "session": "<checkout session>"
  },
  "workflow": "SDK_CHECKOUT",
  "payment_method": {
    "type":"CARD",
    "token": "2cd31999-e44e-4de3-bbe4-179981ff4295"
  }
}

The endpoint response provides the sdk_action_required parameter that defines if additional actions are necessary to conclude the payment:

  • If your customer selects a synchronous payment method, the payment is completed instantly. In this scenario, the field sdk_action_required in the API response will be false, and the payment process concludes at this step.
  • When an additional interaction of the SDK is needed to complete the payment flow, sdk_action_required will be true. If this is the case, you need to follow the instructions from Step 6.

Step 6: Get the 3DS challenge URL

A payment with 3DS may require an additional challenge to check the customer's identity, as described on the 3DS Card Verification (old version) page. If an additional verification step is necessary related to a 3DS verification challenge, the response to the Create Payment endpoint will have the following:

  • A THREE_D_SECURE transaction type.
  • Status equal to PENDING and sub status equal to WAITING_ADDITIONAL_STEP.
  • sdk_action_required = true.

As a result, you need to redirect your customer to the challenge URL. To get the 3DS challenge URL, you need to call the getThreeDSecureChallenge function, providing the checkoutSession used to create the payment. Below, you will find an example of using the getThreeDSecureChallenge function.

fun ApiClientPayment.getThreeDSecureChallenge(
   context: Context,
   // checkout session used to create the payment
   checkoutSession: String? = null,
): SingleLiveEvent<ThreeDSecureChallengeResponse>

The getThreeDSecureChallenge function will return the ThreeDSecureChallengeResponse data class, described in the next code block:

data class ThreeDSecureChallengeResponse(
   val type: String,
   val data: String,
)

The type can return ERROR or URL, defining if the function returned a valid URL for the challenge:

  • If type = URL, data will contain the URL your customer needs to access to complete the 3DS challenge.
  • If type = ERROR, data will contain the error message, informing the source of the problem.

The code block below presents an example of how you can observe the response from ThreeDSecureChallengeResponse:

apiClientPayment.getThreeDSecureChallenge(this)?.observe(this) {
   if (it.type == "URL") {
     // load the URL in web view, custom tab or navigator
   } else 
  		// check the error message to identify the problem
}

The response type is evaluated in the example to determine whether it is a URL. If it is, then you can load it wherever you want. The URL contains a 3D secure challenge. However, if the type is not "URL," it is an error, and you should display your error screen.

You are responsible for redirecting your customers to the URL provided by the getThreeDSecureChallenge(context) function to complete the challenge. Once the customer successfully completes the 3DS challenge, they will be automatically redirected to the callback_url, which you provided when creating the checkout_session with the Create Checkout Session endpoint. To confirm that the challenge has been completed, open a web view and load the URL provided by the getThreeDSecureChallenge(context) function, as shown in the example below:

val webView = WebView(this)
webViewContainer.addView(
   webView,
   ConstraintLayout.LayoutParams(
       ConstraintLayout.LayoutParams.MATCH_PARENT,
       ConstraintLayout.LayoutParams.MATCH_PARENT
   )
)
webView.settings.javaScriptEnabled = true
webView.addJavascriptInterface(
   object {
       @JavascriptInterface
       fun messageFromWeb(data: String?) { // This is required
           // Challenge was completed here
       }
   },
   "Android" // This is required
)
webView.loadUrl(url)

Note that the JavaScript interface must have the same name, messageFromWeb(data : String?), and you must add the name Android. Thus, you can catch the challenge events and know when they finish.

To complete the Headless SDK payment flow, you need to use Yuno Webhooks, which will promptly notify you about the outcome of the 3DS challenge and the final payment status. Using webhooks ensures that you receive real-time updates on the progress of the payment transaction. In addition to the webhooks, you can retrieve the payment information using the Retrieve Payment by ID endpoint.

To finish the payment implementation and understand the remaining steps, access Headless SDK (Payment).

Demo App

In addition to the code examples provided, you can access the Yuno repository for a complete implementation of Yuno Android SDKs.