Skip to content

Commit

Permalink
Expose Klarna property on Source object (#2396)
Browse files Browse the repository at this point in the history
Added unit test
  • Loading branch information
mshafrir-stripe authored Apr 17, 2020
1 parent 423a7c9 commit 50fb89c
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ class KlarnaSourceActivity : AppCompatActivity() {
klarnaParams = KlarnaSourceParams(
purchaseCountry = "UK",
lineItems = LINE_ITEMS,
customPaymentMethods = setOf(
KlarnaSourceParams.CustomPaymentMethods.Installments,
KlarnaSourceParams.CustomPaymentMethods.PayIn4
),
billingFirstName = "Arthur",
billingLastName = "Dent",
billingAddress = Address.Builder()
Expand Down
37 changes: 35 additions & 2 deletions stripe/src/main/java/com/stripe/android/model/Source.kt
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,9 @@ data class Source internal constructor(
@param:Usage @field:Usage @get:Usage
val usage: String? = null,

private val weChatParam: WeChat? = null,
private val _weChat: WeChat? = null,

private val _klarna: Klarna? = null,

/**
* Information about the items and shipping associated with the source. Required for
Expand All @@ -147,7 +149,16 @@ data class Source internal constructor(
"Source type must be '${SourceType.WECHAT}'"
}

return requireNotNull(weChatParam)
return requireNotNull(_weChat)
}

val klarna: Klarna
get() {
check(SourceType.KLARNA == type) {
"Source type must be '${SourceType.KLARNA}'"
}

return requireNotNull(_klarna)
}

@Retention(AnnotationRetention.SOURCE)
Expand Down Expand Up @@ -208,6 +219,28 @@ data class Source internal constructor(
}
}

@Parcelize
data class Klarna(
val firstName: String?,
val lastName: String?,
val purchaseCountry: String?,
val clientToken: String?,
val payNowAssetUrlsDescriptive: String?,
val payNowAssetUrlsStandard: String?,
val payNowName: String?,
val payNowRedirectUrl: String?,
val payLaterAssetUrlsDescriptive: String?,
val payLaterAssetUrlsStandard: String?,
val payLaterName: String?,
val payLaterRedirectUrl: String?,
val payOverTimeAssetUrlsDescriptive: String?,
val payOverTimeAssetUrlsStandard: String?,
val payOverTimeName: String?,
val payOverTimeRedirectUrl: String?,
val paymentMethodCategories: Set<String>,
val customPaymentMethods: Set<String>
) : StripeModel

companion object {
internal const val OBJECT_TYPE = "source"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import androidx.annotation.Size
import com.stripe.android.model.Source
import com.stripe.android.model.SourceTypeModel
import com.stripe.android.model.StripeJsonUtils
import com.stripe.android.model.StripeJsonUtils.optString
import com.stripe.android.model.StripeModel
import org.json.JSONObject

Expand All @@ -16,6 +17,56 @@ internal class SourceJsonParser : ModelJsonParser<Source> {
}
}

internal class KlarnaJsonParser : ModelJsonParser<Source.Klarna> {
override fun parse(json: JSONObject): Source.Klarna {
return Source.Klarna(
firstName = optString(json, FIELD_FIRST_NAME),
lastName = optString(json, FIELD_LAST_NAME),
purchaseCountry = optString(json, FIELD_PURCHASE_COUNTRY),
clientToken = optString(json, FIELD_CLIENT_TOKEN),
payLaterAssetUrlsDescriptive = optString(json, FIELD_PAY_LATER_ASSET_URLS_DESCRIPTIVE),
payLaterAssetUrlsStandard = optString(json, FIELD_PAY_LATER_ASSET_URLS_STANDARD),
payLaterName = optString(json, FIELD_PAY_LATER_NAME),
payLaterRedirectUrl = optString(json, FIELD_PAY_LATER_REDIRECT_URL),
payNowAssetUrlsDescriptive = optString(json, FIELD_PAY_NOW_ASSET_URLS_DESCRIPTIVE),
payNowAssetUrlsStandard = optString(json, FIELD_PAY_NOW_ASSET_URLS_STANDARD),
payNowName = optString(json, FIELD_PAY_NOW_NAME),
payNowRedirectUrl = optString(json, FIELD_PAY_NOW_REDIRECT_URL),
payOverTimeAssetUrlsDescriptive = optString(json, FIELD_PAY_OVER_TIME_ASSET_URLS_DESCRIPTIVE),
payOverTimeAssetUrlsStandard = optString(json, FIELD_PAY_OVER_TIME_ASSET_URLS_STANDARD),
payOverTimeName = optString(json, FIELD_PAY_OVER_TIME_NAME),
payOverTimeRedirectUrl = optString(json, FIELD_PAY_OVER_TIME_REDIRECT_URL),
paymentMethodCategories = parseSet(json, FIELD_PAYMENT_METHOD_CATEGORIES),
customPaymentMethods = parseSet(json, FIELD_CUSTOM_PAYMENT_METHODS)
)
}

private fun parseSet(json: JSONObject, key: String): Set<String> {
return optString(json, key)?.split(",")?.toSet().orEmpty()
}

private companion object {
private const val FIELD_FIRST_NAME = "first_name"
private const val FIELD_LAST_NAME = "last_name"
private const val FIELD_PURCHASE_COUNTRY = "purchase_country"
private const val FIELD_CLIENT_TOKEN = "client_token"
private const val FIELD_PAY_LATER_ASSET_URLS_DESCRIPTIVE = "pay_later_asset_urls_descriptive"
private const val FIELD_PAY_LATER_ASSET_URLS_STANDARD = "pay_later_asset_urls_standard"
private const val FIELD_PAY_LATER_NAME = "pay_later_name"
private const val FIELD_PAY_LATER_REDIRECT_URL = "pay_later_redirect_url"
private const val FIELD_PAY_NOW_ASSET_URLS_DESCRIPTIVE = "pay_now_asset_urls_descriptive"
private const val FIELD_PAY_NOW_ASSET_URLS_STANDARD = "pay_now_asset_urls_standard"
private const val FIELD_PAY_NOW_NAME = "pay_now_name"
private const val FIELD_PAY_NOW_REDIRECT_URL = "pay_now_redirect_url"
private const val FIELD_PAY_OVER_TIME_ASSET_URLS_DESCRIPTIVE = "pay_over_time_asset_urls_descriptive"
private const val FIELD_PAY_OVER_TIME_ASSET_URLS_STANDARD = "pay_over_time_asset_urls_standard"
private const val FIELD_PAY_OVER_TIME_NAME = "pay_over_time_name"
private const val FIELD_PAY_OVER_TIME_REDIRECT_URL = "pay_over_time_redirect_url"
private const val FIELD_PAYMENT_METHOD_CATEGORIES = "payment_method_categories"
private const val FIELD_CUSTOM_PAYMENT_METHODS = "custom_payment_methods"
}
}

private companion object {
private const val VALUE_SOURCE = "source"
private const val VALUE_CARD = "card"
Expand Down Expand Up @@ -43,18 +94,19 @@ internal class SourceJsonParser : ModelJsonParser<Source> {
private const val FIELD_TYPE: String = "type"
private const val FIELD_USAGE: String = "usage"
private const val FIELD_WECHAT: String = "wechat"
private const val FIELD_KLARNA: String = "klarna"

private fun fromCardJson(jsonObject: JSONObject): Source {
return Source(
StripeJsonUtils.optString(jsonObject, FIELD_ID),
optString(jsonObject, FIELD_ID),
sourceTypeModel = SourceCardDataJsonParser().parse(jsonObject),
type = Source.SourceType.CARD,
typeRaw = Source.SourceType.CARD
)
}

private fun fromSourceJson(jsonObject: JSONObject): Source {
@Source.SourceType val typeRaw = StripeJsonUtils.optString(jsonObject, FIELD_TYPE)
@Source.SourceType val typeRaw = optString(jsonObject, FIELD_TYPE)
?: Source.SourceType.UNKNOWN
@Source.SourceType val type = asSourceType(typeRaw)

Expand All @@ -71,16 +123,16 @@ internal class SourceJsonParser : ModelJsonParser<Source> {
}

return Source(
id = StripeJsonUtils.optString(jsonObject, FIELD_ID),
id = optString(jsonObject, FIELD_ID),
amount = StripeJsonUtils.optLong(jsonObject, FIELD_AMOUNT),
clientSecret = StripeJsonUtils.optString(jsonObject, FIELD_CLIENT_SECRET),
clientSecret = optString(jsonObject, FIELD_CLIENT_SECRET),
codeVerification = optStripeJsonModel(
jsonObject,
FIELD_CODE_VERIFICATION
),
created = StripeJsonUtils.optLong(jsonObject, FIELD_CREATED),
currency = StripeJsonUtils.optString(jsonObject, FIELD_CURRENCY),
flow = asSourceFlow(StripeJsonUtils.optString(jsonObject, FIELD_FLOW)),
currency = optString(jsonObject, FIELD_CURRENCY),
flow = asSourceFlow(optString(jsonObject, FIELD_FLOW)),
isLiveMode = jsonObject.optBoolean(FIELD_LIVEMODE),
metaData = StripeJsonUtils.jsonObjectToStringMap(
jsonObject.optJSONObject(FIELD_METADATA)
Expand All @@ -91,19 +143,26 @@ internal class SourceJsonParser : ModelJsonParser<Source> {
sourceOrder = jsonObject.optJSONObject(FIELD_SOURCE_ORDER)?.let {
SourceOrderJsonParser().parse(it)
},
statementDescriptor = StripeJsonUtils.optString(jsonObject, FIELD_STATEMENT_DESCRIPTOR),
status = asSourceStatus(StripeJsonUtils.optString(jsonObject, FIELD_STATUS)),
statementDescriptor = optString(jsonObject, FIELD_STATEMENT_DESCRIPTOR),
status = asSourceStatus(optString(jsonObject, FIELD_STATUS)),
sourceTypeData = sourceTypeData,
sourceTypeModel = sourceTypeModel,
type = type,
typeRaw = typeRaw,
usage = asUsage(StripeJsonUtils.optString(jsonObject, FIELD_USAGE)),
weChatParam = if (Source.SourceType.WECHAT == type) {
usage = asUsage(optString(jsonObject, FIELD_USAGE)),
_weChat = if (Source.SourceType.WECHAT == type) {
WeChatJsonParser().parse(
jsonObject.optJSONObject(FIELD_WECHAT) ?: JSONObject()
)
} else {
null
},
_klarna = if (Source.SourceType.KLARNA == type) {
KlarnaJsonParser().parse(
jsonObject.optJSONObject(FIELD_KLARNA) ?: JSONObject()
)
} else {
null
}
)
}
Expand Down Expand Up @@ -184,6 +243,7 @@ internal class SourceJsonParser : ModelJsonParser<Source> {
Source.SourceType.MULTIBANCO -> Source.SourceType.MULTIBANCO
Source.SourceType.WECHAT -> Source.SourceType.WECHAT
Source.SourceType.UNKNOWN -> Source.SourceType.UNKNOWN
Source.SourceType.KLARNA -> Source.SourceType.KLARNA
else -> Source.SourceType.UNKNOWN
}
}
Expand Down
35 changes: 35 additions & 0 deletions stripe/src/test/java/com/stripe/android/model/SourceFixtures.kt
Original file line number Diff line number Diff line change
Expand Up @@ -471,4 +471,39 @@ internal object SourceFixtures {
}
""".trimIndent()
)

internal val KLARNA = requireNotNull(PARSER.parse(JSONObject(
"""
{
"id": "src_1FfB6GKmrohBAXC",
"object": "source",
"amount": 1000,
"created": 1573848540,
"currency": "eur",
"flow": "redirect",
"livemode": false,
"metadata": {},
"source_order": $SOURCE_ORDER_JSON,
"statement_descriptor": "WIDGET FACTORY",
"status": "pending",
"type": "klarna",
"usage": "single_use",
"klarna": {
"first_name": "Arthur",
"last_name": "Dent",
"purchase_country": "UK",
"client_token": "CLIENT_TOKEN",
"pay_later_asset_urls_descriptive": "https:\/\/x.klarnacdn.net\/payment-method\/assets\/badges\/generic\/klarna.svg",
"pay_later_asset_urls_standard": "https:\/\/x.klarnacdn.net\/payment-method\/assets\/badges\/generic\/klarna.svg",
"pay_later_name": "Pay later in 14 days",
"pay_later_redirect_url": "https:\/\/payment-eu.playground.klarna.com\/8b45xe2",
"pay_over_time_asset_urls_descriptive": "https:\/\/x.klarnacdn.net\/payment-method\/assets\/badges\/generic\/klarna.svg",
"pay_over_time_asset_urls_standard": "https:\/\/x.klarnacdn.net\/payment-method\/assets\/badges\/generic\/klarna.svg",
"pay_over_time_name": "3 interest-free instalments",
"pay_over_time_redirect_url": "https:\/\/payment-eu.playground.klarna.com\/8DA6imn",
"payment_method_categories": "pay_later,pay_over_time"
}
}
""".trimIndent()
)))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.stripe.android.model.parsers

import com.google.common.truth.Truth.assertThat
import com.stripe.android.model.Source
import com.stripe.android.model.SourceFixtures
import org.junit.Test

class SourceJsonParserTest {

@Test
fun parse_shouldReturnExpectedObject() {
assertThat(SourceFixtures.KLARNA.klarna)
.isEqualTo(
Source.Klarna(
firstName = "Arthur",
lastName = "Dent",
purchaseCountry = "UK",
clientToken = "CLIENT_TOKEN",
payLaterAssetUrlsDescriptive = "https://x.klarnacdn.net/payment-method/assets/badges/generic/klarna.svg",
payLaterAssetUrlsStandard = "https://x.klarnacdn.net/payment-method/assets/badges/generic/klarna.svg",
payLaterName = "Pay later in 14 days",
payLaterRedirectUrl = "https://payment-eu.playground.klarna.com/8b45xe2",
payNowAssetUrlsDescriptive = null,
payNowAssetUrlsStandard = null,
payNowName = null,
payNowRedirectUrl = null,
payOverTimeAssetUrlsDescriptive = "https://x.klarnacdn.net/payment-method/assets/badges/generic/klarna.svg",
payOverTimeAssetUrlsStandard = "https://x.klarnacdn.net/payment-method/assets/badges/generic/klarna.svg",
payOverTimeName = "3 interest-free instalments",
payOverTimeRedirectUrl = "https://payment-eu.playground.klarna.com/8DA6imn",
paymentMethodCategories = setOf("pay_later", "pay_over_time"),
customPaymentMethods = emptySet()
)
)
}
}

0 comments on commit 50fb89c

Please sign in to comment.