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

Refactor Source class #2569

Merged
merged 1 commit into from
Jun 10, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 8 additions & 0 deletions MIGRATING.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@
- Changes to `AddPaymentMethodActivity`
- When `CustomerSession` is instantiated with a `stripeAccountId`, it will be used in `AddPaymentMethodActivity`
when creating a payment method
- Changes to `Source`
- `Source.SourceFlow` has been renamed to `Source.Flow` and is now an enum
- `Source.SourceStatus` has been renamed to `Source.Status` and is now an enum
- `Source.Usage` is now an enum
- `SourceCodeVerification` has been moved to `Source.CodeVerification`
- `SourceOwner` has been moved to `Source.Owner`
- `SourceReceiver` has been moved to `Source.Receiver`
- `SourceRedirect` has been moved to `Source.Redirect`
- Changes to `SourceTypeModel.Card`
- `SourceTypeModel.Card.ThreeDSecureStatus` is now an enum
- Changes to `BankAccount`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ class CreateCardSourceActivity : AppCompatActivity() {
* Authenticate the [Source]
*/
private fun authenticateSource(source: Source) {
if (source.flow == Source.SourceFlow.REDIRECT) {
if (source.flow == Source.Flow.Redirect) {
createAuthenticateSourceDialog(source).let {
alertDialog = it
it.show()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ internal class SourcesAdapter : RecyclerView.Adapter<SourcesAdapter.ViewHolder>(
private val viewBinding: SourcesListItemBinding
) : RecyclerView.ViewHolder(viewBinding.root) {
fun bind(source: Source) {
viewBinding.status.text = source.status
viewBinding.status.text = source.status?.toString()
viewBinding.redirectStatus.text = getRedirectStatus(source)
viewBinding.sourceId.text = source.id?.let { sourceId ->
sourceId.substring(sourceId.length - 6)
Expand All @@ -32,7 +32,7 @@ internal class SourcesAdapter : RecyclerView.Adapter<SourcesAdapter.ViewHolder>(
}

private fun getRedirectStatus(source: Source): String? {
return source.redirect?.status
return source.redirect?.status?.toString()
?: (source.sourceTypeModel as SourceTypeModel.Card).threeDSecureStatus.toString()
}
}
Expand Down
4 changes: 2 additions & 2 deletions stripe/src/main/java/com/stripe/android/Stripe.kt
Original file line number Diff line number Diff line change
Expand Up @@ -760,7 +760,7 @@ class Stripe internal constructor(

/**
* Authenticate a [Source] that requires user action via a redirect (i.e. [Source.flow] is
* [Source.SourceFlow.REDIRECT].
* [Source.Flow.Redirect].
*
* The result of this operation will be returned via `Activity#onActivityResult(int, int, Intent)}}`
*
Expand All @@ -784,7 +784,7 @@ class Stripe internal constructor(

/**
* Authenticate a [Source] that requires user action via a redirect (i.e. [Source.flow] is
* [Source.SourceFlow.REDIRECT].
* [Source.Flow.Redirect].
*
* The result of this operation will be returned via `Activity#onActivityResult(int, int, Intent)}}`
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ internal class StripePaymentController internal constructor(
source: Source,
requestOptions: ApiRequest.Options
) {
if (source.flow == Source.SourceFlow.REDIRECT) {
if (source.flow == Source.Flow.Redirect) {
analyticsRequestExecutor.executeAsync(
analyticsRequestFactory.create(
analyticsDataFactory.createAuthSourceParams(
Expand Down
247 changes: 209 additions & 38 deletions stripe/src/main/java/com/stripe/android/model/Source.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.stripe.android.model

import androidx.annotation.StringDef
import com.stripe.android.model.Source.SourceFlow
import com.stripe.android.model.Source.Flow
import com.stripe.android.model.Source.SourceType
import com.stripe.android.model.parsers.SourceJsonParser
import kotlinx.android.parcel.Parcelize
Expand Down Expand Up @@ -37,7 +37,7 @@ data class Source internal constructor(
* Information related to the code verification flow. Present if the source is authenticated
* by a verification code (`flow` is `code_verification`).
*/
val codeVerification: SourceCodeVerification? = null,
val codeVerification: CodeVerification? = null,

/**
* Time at which the object was created. Measured in seconds since the Unix epoch.
Expand All @@ -55,8 +55,7 @@ data class Source internal constructor(
* The authentication `flow` of the source.
* `flow` is one of `redirect`, `receiver`, `code_verification`, `none`.
*/
@param:SourceFlow @field:SourceFlow @get:SourceFlow
val flow: String? = null,
val flow: Flow? = null,

/**
* Has the value true if the object exists in live mode or the value false if the object
Expand All @@ -74,26 +73,25 @@ data class Source internal constructor(
* Information about the owner of the payment instrument that may be used or required by
* particular source types.
*/
val owner: SourceOwner? = null,
val owner: Owner? = null,

/**
* Information related to the receiver flow.
* Present if the source is a receiver ([flow] is [SourceFlow.RECEIVER]).
* Present if the source is a receiver ([flow] is [Flow.Receiver]).
*/
val receiver: SourceReceiver? = null,
val receiver: Receiver? = null,

/**
* Information related to the redirect flow. Present if the source is authenticated by a
* redirect ([flow] is [SourceFlow.REDIRECT]).
* redirect ([flow] is [Flow.REDIRECT]).
*/
val redirect: SourceRedirect? = null,
val redirect: Redirect? = null,

/**
* The status of the source, one of `canceled`, `chargeable`, `consumed`, `failed`,
* or `pending`. Only `chargeable` sources can be used to create a charge.
*/
@param:SourceStatus @field:SourceStatus @get:SourceStatus
val status: String? = null,
val status: Status? = null,

val sourceTypeData: Map<String, @RawValue Any?>? = null,

Expand Down Expand Up @@ -121,8 +119,7 @@ data class Source internal constructor(
* types may or may not be reusable by construction, while others may leave the option at
* creation. If an incompatible value is passed, an error will be returned.
*/
@param:Usage @field:Usage @get:Usage
val usage: String? = null,
val usage: Usage? = null,

private val _weChat: WeChat? = null,

Expand Down Expand Up @@ -183,40 +180,214 @@ data class Source internal constructor(
}
}

@Retention(AnnotationRetention.SOURCE)
@StringDef(SourceStatus.PENDING, SourceStatus.CHARGEABLE, SourceStatus.CONSUMED,
SourceStatus.CANCELED, SourceStatus.FAILED)
annotation class SourceStatus {
companion object {
const val PENDING: String = "pending"
const val CHARGEABLE: String = "chargeable"
const val CONSUMED: String = "consumed"
const val CANCELED: String = "canceled"
const val FAILED: String = "failed"
/**
* The status of the source, one of `canceled`, `chargeable`, `consumed`, `failed`,
* or `pending`. Only `chargeable` sources can be used to create a charge.
*/
enum class Status(private val code: String) {
Canceled("canceled"),
Chargeable("chargeable"),
Consumed("consumed"),
Failed("failed"),
Pending("pending");

override fun toString(): String = code

internal companion object {
fun fromCode(code: String?) = values().firstOrNull { it.code == code }
}
}

@Retention(AnnotationRetention.SOURCE)
@StringDef(Usage.REUSABLE, Usage.SINGLE_USE)
annotation class Usage {
companion object {
const val REUSABLE: String = "reusable"
const val SINGLE_USE: String = "single_use"
/**
* Either `reusable` or `single_use`. Whether this source should be reusable or not.
* Some source types may or may not be reusable by construction, while others may leave the
* option at creation. If an incompatible value is passed, an error will be returned.
*/
enum class Usage(internal val code: String) {
Reusable("reusable"),
SingleUse("single_use");

internal companion object {
fun fromCode(code: String?) = values().firstOrNull { it.code == code }
}
}

@Retention(AnnotationRetention.SOURCE)
@StringDef(SourceFlow.REDIRECT, SourceFlow.RECEIVER, SourceFlow.CODE_VERIFICATION,
SourceFlow.NONE)
annotation class SourceFlow {
companion object {
const val REDIRECT: String = "redirect"
const val RECEIVER: String = "receiver"
const val CODE_VERIFICATION: String = "code_verification"
const val NONE: String = "none"
/**
* The authentication `flow` of the source.
*/
enum class Flow(internal val code: String) {
Redirect("redirect"),
Receiver("receiver"),
CodeVerification("code_verification"),
None("none");

internal companion object {
fun fromCode(code: String?) = values().firstOrNull { it.code == code }
}
}

/**
* Information related to the redirect flow. Present if the source is authenticated by a
* redirect ([flow] is [Flow.Redirect]).
*/
@Parcelize
data class Redirect(
/**
* The URL you provide to redirect the customer to after they authenticated their payment.
*/
val returnUrl: String?,

/**
* The status of the redirect, either
* `pending` (ready to be used by your customer to authenticate the transaction),
* `succeeded` (succesful authentication, cannot be reused) or
* `not_required` (redirect should not be used) or
* `failed` (failed authentication, cannot be reused).
*/
val status: Status?,

/**
* The URL provided to you to redirect a customer to as part of a `redirect`
* authentication flow.
*/
val url: String?
) : StripeModel {

enum class Status(private val code: String) {
Pending("pending"),
Succeeded("succeeded"),
NotRequired("not_required"),
Failed("failed");

override fun toString(): String = code

internal companion object {
fun fromCode(code: String?) = values().firstOrNull { it.code == code }
}
}
}

/**
* Information related to the code verification flow. Present if the source is authenticated
* by a verification code ([flow] is [Flow.CodeVerification]).
*/
@Parcelize
data class CodeVerification internal constructor(
/**
* The number of attempts remaining to authenticate the source object with a verification
* code.
*/
val attemptsRemaining: Int,

/**
* The status of the code verification, either
* `pending` (awaiting verification, `attempts_remaining` should be greater than 0),
* `succeeded` (successful verification) or
* `failed` (failed verification, cannot be verified anymore as `attempts_remaining` should be 0).
*/
val status: Status?
) : StripeModel {

enum class Status(private val code: String) {
Pending("pending"),
Succeeded("succeeded"),
Failed("failed");

internal companion object {
fun fromCode(code: String?) = values().firstOrNull { it.code == code }
}
}
}

/**
* Information related to the receiver flow. Present if [flow] is [Source.Flow.Receiver].
*/
@Parcelize
data class Receiver internal constructor(
/**
* The address of the receiver source. This is the value that should be communicated to the
* customer to send their funds to.
*/
val address: String?,

/**
* The total amount that was moved to your balance. This is almost always equal to the amount
* charged. In rare cases when customers deposit excess funds and we are unable to refund
* those, those funds get moved to your balance and show up in amount_charged as well.
* The amount charged is expressed in the source’s currency.
*/
val amountCharged: Long,

/**
* The total amount received by the receiver source.
* `amount_received = amount_returned + amount_charged` should be true for consumed sources
* unless customers deposit excess funds. The amount received is expressed in the source’s
* currency.
*/
val amountReceived: Long,

/**
* The total amount that was returned to the customer. The amount returned is expressed in
* the source’s currency.
*/
val amountReturned: Long
) : StripeModel

/**
* Information about the owner of the payment instrument that may be used or required by
* particular source types.
*/
@Parcelize
data class Owner internal constructor(
/**
* Owner’s address.
*/
val address: Address?,

/**
* Owner’s email address.
*/
val email: String?,

/**
* Owner’s full name.
*/
val name: String?,

/**
* Owner’s phone number (including extension).
*/
val phone: String?,

/**
* Verified owner’s address. Verified values are verified or provided by the payment
* method directly (and if supported) at the time of authorization or settlement.
* They cannot be set or mutated.
*/
val verifiedAddress: Address?,

/**
* Verified owner’s email address. Verified values are verified or provided by the
* payment method directly (and if supported) at the time of authorization or settlement.
* They cannot be set or mutated.
*/
val verifiedEmail: String?,

/**
* Verified owner’s full name. Verified values are verified or provided by the payment
* method directly (and if supported) at the time of authorization or settlement.
* They cannot be set or mutated.
*/
val verifiedName: String?,

/**
* Verified owner’s phone number (including extension). Verified values are verified or
* provided by the payment method directly (and if supported) at the time of authorization
* or settlement. They cannot be set or mutated.
*/
val verifiedPhone: String?
) : StripeModel

@Parcelize
data class Klarna(
val firstName: String?,
Expand Down
Loading