Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for blik payment intent and payment method #3479

Merged
merged 10 commits into from
Mar 18, 2021
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -521,5 +521,23 @@ data class ConfirmPaymentIntentParams internal constructor(
returnUrl = "stripe://return_url"
)
}

/**
* Create the parameters necessary for confirming a [PaymentIntent] with Blik
*
* @param blikCode code generated from blik mobile banking app
* @param clientSecret client secret from the PaymentIntent that is to be confirmed
*
*/
@JvmStatic
fun createBlik(
blikCode: String,
ccen-stripe marked this conversation as resolved.
Show resolved Hide resolved
clientSecret: String
): ConfirmPaymentIntentParams {
return ConfirmPaymentIntentParams(
clientSecret = clientSecret,
paymentMethodCreateParams = PaymentMethodCreateParams.createBlik(),
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@ data class PaymentMethod internal constructor(
GrabPay("grabpay", isReusable = false),
PayPal("paypal", isReusable = false),
AfterpayClearpay("afterpay_clearpay", isReusable = false),
Netbanking("netbanking", isReusable = false);
Netbanking("netbanking", isReusable = false),
Blik("blik", isReusable = false);

override fun toString(): String {
return code
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,8 @@ data class PaymentMethodCreateParams internal constructor(
PayPal("paypal"),
AfterpayClearpay("afterpay_clearpay"),
Upi("upi"),
Netbanking("netbanking")
Netbanking("netbanking"),
Blik("blik")
}

@Parcelize
Expand Down Expand Up @@ -752,5 +753,18 @@ data class PaymentMethodCreateParams internal constructor(
)
)
}

@JvmStatic
@JvmOverloads
fun createBlik(
billingDetails: PaymentMethod.BillingDetails? = null,
ccen-stripe marked this conversation as resolved.
Show resolved Hide resolved
metadata: Map<String, String>? = null
): PaymentMethodCreateParams {
return PaymentMethodCreateParams(
type = Type.Blik,
billingDetails = billingDetails,
metadata = metadata
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,19 @@ sealed class PaymentMethodOptionsParams(
private const val PARAM_NETWORK = "network"
}
}

@Parcelize
data class Blik(
var code: String,
) : PaymentMethodOptionsParams(PaymentMethod.Type.Blik) {
override fun createTypeParams(): List<Pair<String, Any?>> {
return listOf(
PARAM_CODE to code,
)
}

companion object {
const val PARAM_CODE = "code"
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private companion object

Copy link
Contributor Author

@ccen-stripe ccen-stripe Mar 17, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

made this internal so that test could access them, please lmk if this is necessary, happy to change otherwise :)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm!

}
}
16 changes: 14 additions & 2 deletions stripe/src/main/java/com/stripe/android/model/StripeIntent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ interface StripeIntent : StripeModel {
RedirectToUrl("redirect_to_url"),
UseStripeSdk("use_stripe_sdk"),
DisplayOxxoDetails("display_oxxo_details"),
AlipayRedirect("alipay_handle_redirect");
AlipayRedirect("alipay_handle_redirect"),
BlikAuthorize("blik_authorize");

override fun toString(): String {
return code
Expand Down Expand Up @@ -130,7 +131,8 @@ interface StripeIntent : StripeModel {
}
}

sealed class NextActionData : StripeModel {
sealed class NextActionData() : StripeModel {
ccen-stripe marked this conversation as resolved.
Show resolved Hide resolved

@Parcelize
data class DisplayOxxoDetails(
/**
Expand Down Expand Up @@ -220,5 +222,15 @@ interface StripeIntent : StripeModel {
) : Parcelable
}
}

@Parcelize
object BlikAuthorize : NextActionData() {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mshafrir-stripe

This falls into the else case in StripPaymentController as the SDK doesn't need to do anything upon seeing `"next_action": {
"type": "blik_authorize"
},
Is this correct behavior?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That seems fine for now.

override fun hashCode(): Int {
return 0
}
override fun equals(other: Any?): Boolean {
return this === other
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ internal class NextActionDataParser : ModelJsonParser<StripeIntent.NextActionDat
StripeIntent.NextActionType.RedirectToUrl -> RedirectToUrlParser()
StripeIntent.NextActionType.UseStripeSdk -> SdkDataJsonParser()
StripeIntent.NextActionType.AlipayRedirect -> AlipayRedirectParser()
StripeIntent.NextActionType.BlikAuthorize -> BlikAuthorizeParser()
else -> return null
}
return parser.parse(json.optJSONObject(nextActionType.code) ?: JSONObject())
Expand Down Expand Up @@ -142,6 +143,13 @@ internal class NextActionDataParser : ModelJsonParser<StripeIntent.NextActionDat
}
}

internal class BlikAuthorizeParser :
ModelJsonParser<StripeIntent.NextActionData.BlikAuthorize> {
override fun parse(json: JSONObject): StripeIntent.NextActionData.BlikAuthorize {
return StripeIntent.NextActionData.BlikAuthorize
}
}

private companion object {
private const val FIELD_NEXT_ACTION_TYPE = "type"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ internal object ApiKeyFixtures {
const val AFTERPAY_PUBLISHABLE_KEY = "pk_test_vOo1umqsYxSrP5UXfOeL3ecm"
const val UPI_PUBLISHABLE_KEY = "pk_test_51H7wmsBte6TMTRd4gph9Wm7gnQOKJwdVTCj30AhtB8MhWtlYj6v9xDn1vdCtKYGAE7cybr6fQdbQQtgvzBihE9cl00tOnrTpL9"
const val NETBANKING_PUBLISHABLE_KEY = "pk_test_51H7wmsBte6TMTRd4gph9Wm7gnQOKJwdVTCj30AhtB8MhWtlYj6v9xDn1vdCtKYGAE7cybr6fQdbQQtgvzBihE9cl00tOnrTpL9"
const val BLIK_PUBLISHABLE_KEY = "pk_test_ErsyMEOTudSjQR8hh0VrQr5X008sBXGOu6"
}
Original file line number Diff line number Diff line change
Expand Up @@ -314,4 +314,14 @@ internal class PaymentMethodEndToEndTest {
assertThat(missingAddressException.message)
.isEqualTo("Missing required param: billing_details[address][line1].")
}

@Test
fun createPaymentMethod_withBlik_shouldCreateObject() {
val params = PaymentMethodCreateParams.createBlik()
val paymentMethod =
Stripe(context, ApiKeyFixtures.BLIK_PUBLISHABLE_KEY)
.createPaymentMethodSynchronous(params)
assertThat(paymentMethod?.type)
.isEqualTo(PaymentMethod.Type.Blik)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ class ConfirmPaymentIntentParamsTest {
}

@Test
fun toParamMap_withPaymentMethodOptions_shouldCreateExpectedMap() {
fun toParamMap_withCardPaymentMethodOptions_shouldCreateExpectedMap() {
assertThat(
ConfirmPaymentIntentParams(
paymentMethodId = "pm_123",
Expand All @@ -324,6 +324,31 @@ class ConfirmPaymentIntentParamsTest {
)
}

@Test
fun toParamMap_withBlikPaymentMethodOptions_shouldCreateExpectedMap() {
val blikCode = "123456"
assertThat(
ConfirmPaymentIntentParams(
paymentMethodId = "pm_123",
paymentMethodOptions = PaymentMethodOptionsParams.Blik(
code = blikCode
),
clientSecret = CLIENT_SECRET
).toParamMap()
).isEqualTo(
mapOf(
"payment_method" to "pm_123",
"payment_method_options" to mapOf(
PaymentMethod.Type.Blik.code to mapOf(
PaymentMethodOptionsParams.Blik.PARAM_CODE to blikCode
)
),
"client_secret" to CLIENT_SECRET,
"use_stripe_sdk" to false
)
)
}

@Test
fun toParamMap_withReceiptEmail_shouldCreateExpectedMap() {
assertThat(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -872,4 +872,70 @@ internal object PaymentIntentFixtures {
)

val ALIPAY_TEST_MODE = PARSER.parse(ALIPAY_TEST_MODE_JSON)!!

val PI_REQUIRES_BLIK_AUTHORIZE_JSON = JSONObject(
"""
{
"id": "pi_1IVmwXFY0qyl6XeWwxGWA04D",
"object": "payment_intent",
"amount": 1099,
"amount_capturable": 0,
"amount_received": 0,
"amount_subtotal": 1099,
"application": null,
"application_fee_amount": null,
"canceled_at": null,
"cancellation_reason": null,
"capture_method": "automatic",
"charges": {
"object": "list",
"data": [

],
"has_more": false,
"total_count": 0,
"url": "/v1/charges?payment_intent=pi_1IVmwXFY0qyl6XeWwxGWA04D"
},
"client_secret": "pi_1IVmwXFY0qyl6XeWwxGWA04D_secret_4U8cSCdPefr8LHtPsKvA3mcQz",
"confirmation_method": "automatic",
"created": 1615939737,
"currency": "pln",
"customer": null,
"description": null,
"invoice": null,
"last_payment_error": null,
"livemode": false,
"metadata": {
},
"next_action": {
"type": "blik_authorize"
},
"on_behalf_of": null,
"payment_method": "pm_1IVnI3FY0qyl6XeWxJFdBh2g",
"payment_method_options": {
"blik": {
}
},
"payment_method_types": [
"blik"
],
"receipt_email": null,
"review": null,
"setup_future_usage": null,
"shipping": null,
"source": null,
"statement_descriptor": null,
"statement_descriptor_suffix": null,
"status": "requires_action",
"total_details": {
"amount_discount": 0,
"amount_tax": 0
},
"transfer_data": null,
"transfer_group": null
}
""".trimIndent()
)

val PI_REQUIRES_BLIK_AUTHORIZE = PARSER.parse(PI_REQUIRES_BLIK_AUTHORIZE_JSON)!!
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class PaymentIntentTest {
}

@Test
fun parsePaymentIntentWithPaymentMethods() {
fun parsePaymentIntentWith3DS2PaymentMethods() {
val paymentIntent = PaymentIntentFixtures.PI_REQUIRES_MASTERCARD_3DS2
assertThat(paymentIntent.requiresAction())
.isTrue()
Expand All @@ -39,6 +39,15 @@ class PaymentIntentTest {
.isNull()
}

@Test
fun parsePaymentIntentWithBlikPaymentMethods() {
val paymentIntent = PaymentIntentFixtures.PI_REQUIRES_BLIK_AUTHORIZE
assertThat(paymentIntent.requiresAction())
.isTrue()
assertThat(paymentIntent.paymentMethodTypes)
.containsExactly("blik")
}

@Test
fun getNextActionData_whenUseStripeSdkWith3ds2() {
val paymentIntent = PaymentIntentFixtures.PI_REQUIRES_MASTERCARD_3DS2
Expand Down Expand Up @@ -74,6 +83,13 @@ class PaymentIntentTest {
.isEqualTo("stripe://deeplink")
}

@Test
fun getNextActionData_whenBlikAuthorize() {
val paymentIntent = PaymentIntentFixtures.PI_REQUIRES_BLIK_AUTHORIZE
assertThat(paymentIntent.nextActionData)
.isInstanceOf(StripeIntent.NextActionData.BlikAuthorize::class.java)
}

@Test
fun getLastPaymentError_parsesCorrectly() {
val lastPaymentError =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,22 @@ class PaymentMethodOptionsParamsTest {
)
}

@Test
fun blikToParamMap_withCode_includeCode() {
val blikCode = "123456"
assertThat(
PaymentMethodOptionsParams.Blik(
code = blikCode
).toParamMap()
).isEqualTo(
mapOf(
PaymentMethod.Type.Blik.code to mapOf(
PaymentMethodOptionsParams.Blik.PARAM_CODE to blikCode
)
)
)
}

@Test
fun cardToParamMap_withNoData_shouldHaveEmptyParams() {
assertThat(
Expand Down