# BLIK payments Learn how to accept BLIK, a common payment method in Poland. # Android BLIK is a [single use](https://docs.stripe.com/payments/payment-methods.md#usage) payment method that requires customers to [authenticate](https://docs.stripe.com/payments/payment-methods.md#customer-actions) their payments. When customers want to pay online using BLIK, they request a six-digit code from their banking application and enter it into the payment collection form. The bank sends a push notification to your customer’s mobile phone asking to authorize the payment inside their banking application. The BLIK code is valid for 2 minutes; customers have 60 seconds to authorize the payment after starting a payment. After 60 seconds, it times out and they must request a new BLIK code. Customers typically approve BLIK payments in less than 10 seconds. ## Set up Stripe [Server-side] [Client-side] First, you need a Stripe account. [Register now](https://dashboard.stripe.com/register). ### Server-side This integration requires endpoints on your server that talk to the Stripe API. Use the official libraries for access to the Stripe API from your server: #### Ruby ```bash # Available as a gem sudo gem install stripe ``` ```ruby # If you use bundler, you can add this line to your Gemfile gem 'stripe' ``` ### Client-side The [Stripe Android SDK](https://github.com/stripe/stripe-android) is open source and [fully documented](https://stripe.dev/stripe-android/). To install the SDK, add `stripe-android` to the `dependencies` block of your [app/build.gradle](https://developer.android.com/studio/build/dependencies) file: #### Kotlin ```kotlin plugins { id("com.android.application") } android { ... } dependencies { // ... // Stripe Android SDK implementation("com.stripe:stripe-android:23.9.2") // Include the financial connections SDK to support US bank account as a payment method implementation("com.stripe:financial-connections:23.9.2") } ``` > For details on the latest SDK release and past versions, see the [Releases](https://github.com/stripe/stripe-android/releases) page on GitHub. To receive notifications when a new release is published, [watch releases for the repository](https://docs.github.com/en/github/managing-subscriptions-and-notifications-on-github/configuring-notifications#configuring-your-watch-settings-for-an-individual-repository). Configure the SDK with your Stripe [publishable key](https://dashboard.stripe.com/apikeys) so that it can make requests to the Stripe API, such as in your `Application` subclass: #### Kotlin ```kotlin import com.stripe.android.PaymentConfiguration class MyApp : Application() { override fun onCreate() { super.onCreate() PaymentConfiguration.init( applicationContext, "<>" ) } } ``` > Use your [test keys](https://docs.stripe.com/keys.md#obtain-api-keys) while you test and develop, and your [live mode](https://docs.stripe.com/keys.md#test-live-modes) keys when you publish your app. Stripe samples also use [OkHttp](https://github.com/square/okhttp) and [GSON](https://github.com/google/gson) to make HTTP requests to a server. ## Create a PaymentIntent [Server-side] [Client-side] A [PaymentIntent](https://docs.stripe.com/api/payment_intents/object.md) is an object that represents your intent to collect payment from a customer and tracks the lifecycle of the payment process through each stage. ### Server-side First, create a `PaymentIntent` on your server and specify the amount to collect and the `pln` currency (BLIK doesn’t support other currencies). Add `blik` to the list of [payment method types](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_types) for your `PaymentIntent`. ```curl curl https://api.stripe.com/v1/payment_intents \ -u "<>:" \ -d amount=1099 \ -d currency=pln \ -d "payment_method_types[]=blik" \ --data-urlencode "description=A description of what you're selling" \ -d statement_descriptor=ORDER_123 ``` ### Client-side On the client, request a PaymentIntent from your server and store its client secret. #### Kotlin ```kotlin class BlikPaymentActivity: AppCompatActivity() { private lateinit var paymentIntentClientSecret: String override fun onCreate(savedInstanceState: Bundle?) { // ... startCheckout() } private fun startCheckout() { // Request a PaymentIntent from your server and store its client secret } } ``` ## Collect payment method details [Client-side] In your app, collect the required 6-digit BLIK code from the customer. Create a `PaymentMethodOptionsParams` with the BLIK code. #### Kotlin ```kotlin val paymentMethodOptionsParams = PaymentMethodOptionsParams.Blik(code = "777123"), ``` ## Submit the payment to Stripe [Client-side] Retrieve the client secret from the PaymentIntent you created in step 2 and the 6-digit BLIK code you collected in step 3 and call [PaymentLauncher confirm](https://stripe.dev/stripe-android/payments-core/com.stripe.android.payments.paymentlauncher/-payment-launcher/index.html#74063765%2FFunctions%2F-1622557690). #### Kotlin ```kotlin class BlikPaymentActivity : AppCompatActivity() { // ... private lateinit var paymentIntentClientSecret: String private val paymentLauncher: PaymentLauncher by lazy { PaymentLauncher.Companion.create( this, PaymentConfiguration.getInstance(applicationContext).publishableKey, PaymentConfiguration.getInstance(applicationContext).stripeAccountId, ::onPaymentResult ) } private fun startCheckout() { // ... val confirmParams = ConfirmPaymentIntentParams .createWithPaymentMethodCreateParams( paymentMethodCreateParams = PaymentMethodCreateParams.createBlik(), clientSecret = paymentIntentClientSecret, paymentMethodOptions = paymentMethodOptionsParams ) paymentLauncher.confirm(confirmParams) } private fun onPaymentResult(paymentResult: PaymentResult) { val message = when (paymentResult) { is PaymentResult.Completed -> { // Show a timer or a dialog to your customer to prompt them to authorize the payment. // Poll your server or Stripe API for the PaymentIntent status update } is PaymentResult.Canceled -> { "Canceled!" } is PaymentResult.Failed -> { // This string comes from the PaymentIntent's error message. // See here: https://stripe.com/docs/api/payment_intents/object#payment_intent_object-last_payment_error-message "Failed: " + paymentResult.throwable.message } } } } ``` After confirmation with valid parameters, the status of the PaymentIntent becomes `requires_action` and `onPaymentResult` is called with `PaymentResult.Completed`. The `next_action` has the `blik_authorize` type: this means that the next step is completed by your customer, who has 60 seconds to confirm the payment inside their mobile banking app. We recommend: - Showing a timer or pop-up to your customer to prompt them to authorise the payment - Polling your server or Stripe API for the PaymentIntent status update - Displaying the payment confirmation to your customer as soon as the PaymentIntent status updates to `succeeded` ## Optional: Handle post-payment events Stripe sends a [payment_intent.succeeded](https://docs.stripe.com/api/events/types.md#event_types-payment_intent.succeeded) event when the payment completes. Use the Dashboard, a custom *webhook* (A webhook is a real-time push notification sent to your application as a JSON payload through HTTPS requests), or a partner solution to receive these events and run actions, like sending an order confirmation email to your customer, logging the sale in a database, or starting a shipping workflow. Listen for these events rather than waiting on a callback from the client. On the client, the customer could close the browser window or quit the app before the callback executes, and malicious clients could manipulate the response. Setting up your integration to listen for asynchronous events also helps you accept more payment methods in the future. Learn about the [differences between all supported payment methods](https://stripe.com/payments/payment-methods-guide). - **Handle events manually in the Dashboard** Use the Dashboard to [View your test payments in the Dashboard](https://dashboard.stripe.com/test/payments), send email receipts, handle payouts or retry failed payments. - **Build a custom webhook** [Build a custom webhook](https://docs.stripe.com/webhooks/handling-payment-events.md#build-your-own-webhook) handler to listen for events and build custom asynchronous payment flows. Test and debug your webhook integration locally with the Stripe CLI. - **Integrate a prebuilt app** Handle common business events, such as [automation](https://stripe.partners/?f_category=automation) or [marketing and sales](https://stripe.partners/?f_category=marketing-and-sales), by integrating a partner application. ## Optional: Simulate failures in a sandbox BLIK payments can fail for different reasons. There are immediate failures (for example, the code is expired or invalid), delayed errors (the bank declines) or timeouts (the customer didn’t respond in time). You can simulate each failure scenarios by passing `email` values matching certain patterns (documented below) when creating the `PaymentIntent`, as part of the [billing details](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_data-billing_details), with any 6-digit as BLIK code. ### Immediate Failures | Failure code | Explanation | Email pattern | | ---------------------------------- | -------------------------------------------------------------------------------------------------------------- | ------------------- | | `payment_method_invalid_parameter` | The code passed wasn’t a valid BLIK code. | `.*invalid_code@.*` | | `payment_method_invalid_parameter` | The code passed has expired. BLIK codes expire after 2 minutes, please request another code from the customer. | `.*expired_code@.*` | ### Declines (8 second delay) | Failure code | Explanation | Email pattern | | --------------------------------- | ---------------------------------------------------------------------------------------------- | ------------------------- | | `payment_method_provider_decline` | The payment has been declined because the payment limit on this bank account has been reached. | `.*limit_exceeded@.*` | | `payment_method_provider_decline` | The bank account has insufficient funds to complete the purchase. | `.*insufficient_funds@.*` | | `payment_method_provider_decline` | The customer declined this payment. | `.*customer_declined@.*` | | `payment_method_provider_decline` | The payment has been declined by the customer’s bank for an unknown reason. | `.*bank_declined@.*` | | `payment_method_provider_decline` | The payment has been declined for an unknown reason. | `.*blik_declined@.*` | ### Timeouts (60 second delay) | Failure code | Explanation | Email pattern | | --------------------------------- | ------------------------------------------------------------------------- | ----------------------- | | `payment_method_provider_timeout` | The customer didn’t approve this payment within the allocated 60 seconds. | `.*customer_timeout@.*` | | `payment_method_provider_timeout` | The request to the customer’s bank timed out. | `.*bank_timeout@.*` | | `payment_method_provider_timeout` | The request to the BLIK network timed out. | `.*blik_timeout@.*` |