From d7bf017056c7eb9cc4f45b2df9bdf82613119b6a Mon Sep 17 00:00:00 2001 From: Michael Shafrir Date: Wed, 8 Jan 2020 17:35:08 -0500 Subject: [PATCH 1/2] Add support for creating a person token Summary - Add `Stripe#createPersonToken()` to create a person token asynchronously - Add `Stripe#createPersonTokenSynchronous()` to create a person token synchronously See https://stripe.com/docs/api/tokens/create_person Motivation ANDROID-462 Testing Add unit tests --- .../stripe/android/AnalyticsDataFactory.kt | 2 +- .../main/java/com/stripe/android/Stripe.kt | 64 +++ .../android/model/AddressJapanParams.kt | 120 ++++ .../com/stripe/android/model/DateOfBirth.kt | 16 +- .../stripe/android/model/PersonTokenParams.kt | 535 ++++++++++++++++++ .../java/com/stripe/android/model/Token.kt | 3 +- .../android/model/parsers/TokenJsonParser.kt | 3 +- .../java/com/stripe/android/StripeTest.java | 20 + .../model/PersonTokenParamsFixtures.java | 39 ++ 9 files changed, 798 insertions(+), 4 deletions(-) create mode 100644 stripe/src/main/java/com/stripe/android/model/AddressJapanParams.kt create mode 100644 stripe/src/main/java/com/stripe/android/model/PersonTokenParams.kt create mode 100644 stripe/src/test/java/com/stripe/android/model/PersonTokenParamsFixtures.java diff --git a/stripe/src/main/java/com/stripe/android/AnalyticsDataFactory.kt b/stripe/src/main/java/com/stripe/android/AnalyticsDataFactory.kt index 861a4d16cbf..e0d348cea1b 100644 --- a/stripe/src/main/java/com/stripe/android/AnalyticsDataFactory.kt +++ b/stripe/src/main/java/com/stripe/android/AnalyticsDataFactory.kt @@ -142,7 +142,7 @@ internal class AnalyticsDataFactory @VisibleForTesting internal constructor( internal fun getTokenCreationParams( productUsageTokens: List?, publishableKey: String, - tokenType: String? + @Token.TokenType tokenType: String? ): Map { return getEventLoggingParams( EventName.TOKEN_CREATION, diff --git a/stripe/src/main/java/com/stripe/android/Stripe.kt b/stripe/src/main/java/com/stripe/android/Stripe.kt index 9132acfe0b8..1fba363958b 100644 --- a/stripe/src/main/java/com/stripe/android/Stripe.kt +++ b/stripe/src/main/java/com/stripe/android/Stripe.kt @@ -22,6 +22,7 @@ import com.stripe.android.model.CvcTokenParams import com.stripe.android.model.PaymentIntent import com.stripe.android.model.PaymentMethod import com.stripe.android.model.PaymentMethodCreateParams +import com.stripe.android.model.PersonTokenParams import com.stripe.android.model.PiiTokenParams import com.stripe.android.model.SetupIntent import com.stripe.android.model.Source @@ -1155,6 +1156,69 @@ class Stripe internal constructor( ) } + /** + * Creates a single-use token that represents the details for a person. Use this when creating or + * updating persons associated with a Connect account. + * See [the documentation](https://stripe.com/docs/connect/account-tokens) to learn more. + * + * Person tokens may be created only in live mode, with your application’s publishable key. + * Your application’s secret key may be used to create person tokens only in test mode. + * + * See [Create a person token](https://stripe.com/docs/api/tokens/create_person) + * + * @param params the person token creation params + * @param idempotencyKey optional, see [Idempotent Requests](https://stripe.com/docs/api/idempotent_requests) + * @param callback a [ApiResultCallback] to receive the result or error + */ + @UiThread + @JvmOverloads + fun createPersonToken( + params: PersonTokenParams, + idempotencyKey: String? = null, + callback: ApiResultCallback + ) { + createTokenFromParams( + params.toParamMap(), + Token.TokenType.PERSON, + idempotencyKey, + callback + ) + } + + /** + * Creates a single-use token that represents the details for a person. Use this when creating or + * updating persons associated with a Connect account. + * See [the documentation](https://stripe.com/docs/connect/account-tokens) to learn more. + * + * Person tokens may be created only in live mode, with your application’s publishable key. + * Your application’s secret key may be used to create person tokens only in test mode. + * + * See [Create a person token](https://stripe.com/docs/api/tokens/create_person) + * + * @param params the person token creation params + * @param idempotencyKey optional, see [Idempotent Requests](https://stripe.com/docs/api/idempotent_requests) + * + * @return a [Token] representing the person + */ + @Throws(AuthenticationException::class, InvalidRequestException::class, + APIConnectionException::class, CardException::class, APIException::class) + @WorkerThread + @JvmOverloads + fun createPersonTokenSynchronous( + params: PersonTokenParams, + idempotencyKey: String? = null + ): Token? { + return stripeRepository.createToken( + params.toParamMap(), + ApiRequest.Options( + apiKey = publishableKey, + stripeAccount = stripeAccountId, + idempotencyKey = idempotencyKey + ), + Token.TokenType.PERSON + ) + } + private fun createTokenFromParams( tokenParams: Map, @Token.TokenType tokenType: String, diff --git a/stripe/src/main/java/com/stripe/android/model/AddressJapanParams.kt b/stripe/src/main/java/com/stripe/android/model/AddressJapanParams.kt new file mode 100644 index 00000000000..4f8c259f5f4 --- /dev/null +++ b/stripe/src/main/java/com/stripe/android/model/AddressJapanParams.kt @@ -0,0 +1,120 @@ +package com.stripe.android.model + +import android.os.Parcelable +import com.stripe.android.ObjectBuilder +import java.util.Locale +import kotlinx.android.parcel.Parcelize + +@Parcelize +data class AddressJapanParams( + /** + * City or ward. + */ + val city: String? = null, + + /** + * Two-letter country code (ISO 3166-1 alpha-2). + */ + val country: String? = null, + + /** + * Block or building number. + */ + val line1: String? = null, + + /** + * Building details. + */ + val line2: String? = null, + + /** + * Postal code. + */ + val postalCode: String? = null, + + /** + * Prefecture. + */ + val state: String? = null, + + /** + * Town or cho-me. + */ + val town: String? = null +) : StripeParamsModel, Parcelable { + override fun toParamMap(): Map { + return listOf( + PARAM_CITY to city, + PARAM_COUNTRY to country, + PARAM_LINE_1 to line1, + PARAM_LINE_2 to line2, + PARAM_POSTAL_CODE to postalCode, + PARAM_STATE to state, + PARAM_TOWN to town + ).fold(emptyMap()) { acc, (key, value) -> + acc.plus( + value?.let { mapOf(key to it) }.orEmpty() + ) + } + } + + class Builder : ObjectBuilder { + private var city: String? = null + private var country: String? = null + private var line1: String? = null + private var line2: String? = null + private var postalCode: String? = null + private var state: String? = null + private var town: String? = null + + fun setCity(city: String?): Builder = apply { + this.city = city + } + + fun setCountry(country: String?): Builder = apply { + this.country = country?.toUpperCase(Locale.ROOT) + } + + fun setLine1(line1: String?): Builder = apply { + this.line1 = line1 + } + + fun setLine2(line2: String?): Builder = apply { + this.line2 = line2 + } + + fun setPostalCode(postalCode: String?): Builder = apply { + this.postalCode = postalCode + } + + fun setState(state: String?): Builder = apply { + this.state = state + } + + fun setTown(town: String?): Builder = apply { + this.town = town + } + + override fun build(): AddressJapanParams { + return AddressJapanParams( + city = city, + country = country, + line1 = line1, + line2 = line2, + postalCode = postalCode, + state = state, + town = town + ) + } + } + + private companion object { + private const val PARAM_CITY = "city" + private const val PARAM_COUNTRY = "country" + private const val PARAM_LINE_1 = "line1" + private const val PARAM_LINE_2 = "line2" + private const val PARAM_POSTAL_CODE = "postal_code" + private const val PARAM_STATE = "state" + private const val PARAM_TOWN = "town" + } +} diff --git a/stripe/src/main/java/com/stripe/android/model/DateOfBirth.kt b/stripe/src/main/java/com/stripe/android/model/DateOfBirth.kt index 73e62342bf1..d0f2302b56f 100644 --- a/stripe/src/main/java/com/stripe/android/model/DateOfBirth.kt +++ b/stripe/src/main/java/com/stripe/android/model/DateOfBirth.kt @@ -8,4 +8,18 @@ data class DateOfBirth( val day: Int, val month: Int, val year: Int -) : Parcelable +) : StripeParamsModel, Parcelable { + override fun toParamMap(): Map { + return mapOf( + PARAM_DAY to day, + PARAM_MONTH to month, + PARAM_YEAR to year + ) + } + + private companion object { + private const val PARAM_DAY = "day" + private const val PARAM_MONTH = "month" + private const val PARAM_YEAR = "year" + } +} diff --git a/stripe/src/main/java/com/stripe/android/model/PersonTokenParams.kt b/stripe/src/main/java/com/stripe/android/model/PersonTokenParams.kt new file mode 100644 index 00000000000..eb7ef132e0c --- /dev/null +++ b/stripe/src/main/java/com/stripe/android/model/PersonTokenParams.kt @@ -0,0 +1,535 @@ +package com.stripe.android.model + +import android.os.Parcelable +import com.stripe.android.ObjectBuilder +import kotlinx.android.parcel.Parcelize + +/** + * Creates a single-use token that represents the details for a person. Use this when creating or + * updating persons associated with a Connect account. + * See [the documentation](https://stripe.com/docs/connect/account-tokens) to learn more. + * + * Person tokens may be created only in live mode, with your application’s publishable key. + * Your application’s secret key may be used to create person tokens only in test mode. + * + * See [Create a person token](https://stripe.com/docs/api/tokens/create_person) + */ +@Parcelize +data class PersonTokenParams( + /** + * The person’s address. + * + * [person.address](https://stripe.com/docs/api/tokens/create_person#create_person_token-person-address) + */ + val address: Address? = null, + + /** + * The Kana variation of the person’s address (Japan only). + * + * [person.address_kana](https://stripe.com/docs/api/tokens/create_person#create_person_token-person-address_kana) + */ + val addressKana: AddressJapanParams? = null, + + /** + * The Kanji variation of the person’s address (Japan only). + * + * [person.address_kanji](https://stripe.com/docs/api/tokens/create_person#create_person_token-person-address_kanji) + */ + val addressKanji: AddressJapanParams? = null, + + /** + * The person’s date of birth. + * + * [person.dob](https://stripe.com/docs/api/tokens/create_person#create_person_token-person-dob) + */ + val dateOfBirth: DateOfBirth? = null, + + /** + * The person’s email address. + * + * [person.email](https://stripe.com/docs/api/tokens/create_person#create_person_token-person-email) + */ + val email: String? = null, + + /** + * The person’s first name. + * + * [person.first_name](https://stripe.com/docs/api/tokens/create_person#create_person_token-person-first_name) + */ + val firstName: String? = null, + + /** + * The Kana variation of the person’s first name (Japan only). + * + * [person.first_name_kana](https://stripe.com/docs/api/tokens/create_person#create_person_token-person-first_name_kana) + */ + val firstNameKana: String? = null, + + /** + * The Kanji variation of the person’s first name (Japan only). + * + * [person.first_name_kanji](https://stripe.com/docs/api/tokens/create_person#create_person_token-person-first_name_kanji) + */ + val firstNameKanji: String? = null, + + /** + * The person’s gender (International regulations require either “male” or “female”). + * + * [person.gender](https://stripe.com/docs/api/tokens/create_person#create_person_token-person-gender) + */ + val gender: String? = null, + + /** + * The person’s ID number, as appropriate for their country. For example, a social security + * number in the U.S., social insurance number in Canada, etc. Instead of the number itself, + * you can also provide a PII token. + * + * [person.id_number](https://stripe.com/docs/api/tokens/create_person#create_person_token-person-id_number) + */ + val idNumber: String? = null, + + /** + * The person’s last name. + * + * [person.last_name](https://stripe.com/docs/api/tokens/create_person#create_person_token-person-last_name) + */ + val lastName: String? = null, + + /** + * The Kana variation of the person’s last name (Japan only). + * + * [person.last_name_kana](https://stripe.com/docs/api/tokens/create_person#create_person_token-person-last_name_kana) + */ + val lastNameKana: String? = null, + + /** + * The Kanji variation of the person’s last name (Japan only). + * + * [person.last_name_kanji](https://stripe.com/docs/api/tokens/create_person#create_person_token-person-last_name_kanji) + */ + val lastNameKanji: String? = null, + + /** + * The person’s maiden name. + * + * [person.maiden_name](https://stripe.com/docs/api/tokens/create_person#create_person_token-person-maiden_name) + */ + val maidenName: String? = null, + + /** + * Set of key-value pairs that you can attach to an object. This can be useful for storing + * additional information about the object in a structured format. Individual keys can be unset + * by posting an empty value to them. All keys can be unset by posting an empty value + * to `metadata`. + * + * [person.metadata](https://stripe.com/docs/api/tokens/create_person#create_person_token-person-metadata) + */ + val metadata: Map? = null, + + /** + * The person’s phone number. + * + * [person.phone](https://stripe.com/docs/api/tokens/create_person#create_person_token-person-phone) + */ + val phone: String? = null, + + /** + * The relationship that this person has with the account’s legal entity. + * + * [person.relationship](https://stripe.com/docs/api/tokens/create_person#create_person_token-person-relationship) + */ + val relationship: Relationship? = null, + + /** + * The last 4 digits of the person’s social security number. + * + * [person.ssn_last_4](https://stripe.com/docs/api/tokens/create_person#create_person_token-person-ssn_last_4) + */ + val ssnLast4: String? = null, + + /** + * The person’s verification status. + * + * [person.verification](https://stripe.com/docs/api/tokens/create_person#create_person_token-person-verification) + */ + val verification: Verification? = null +) : StripeParamsModel, Parcelable { + override fun toParamMap(): Map { + return mapOf(PARAM_PERSON to + listOf( + PARAM_ADDRESS to address?.toParamMap(), + PARAM_ADDRESS_KANA to addressKana?.toParamMap(), + PARAM_ADDRESS_KANJI to addressKanji?.toParamMap(), + PARAM_DOB to dateOfBirth?.toParamMap(), + PARAM_EMAIL to email, + PARAM_FIRST_NAME to firstName, + PARAM_FIRST_NAME_KANA to firstNameKana, + PARAM_FIRST_NAME_KANJI to firstNameKanji, + PARAM_GENDER to gender, + PARAM_ID_NUMBER to idNumber, + PARAM_LAST_NAME to lastName, + PARAM_LAST_NAME_KANA to lastNameKana, + PARAM_LAST_NAME_KANJI to lastNameKanji, + PARAM_MAIDEN_NAME to maidenName, + PARAM_METADATA to metadata, + PARAM_PHONE to phone, + PARAM_RELATIONSHIP to relationship?.toParamMap(), + PARAM_SSN_LAST_4 to ssnLast4, + PARAM_VERIFICATION to verification?.toParamMap() + ).fold(emptyMap()) { acc, (key, value) -> + acc.plus( + value?.let { mapOf(key to it) }.orEmpty() + ) + } + ) + } + + /** + * The relationship that this person has with the account’s legal entity. + * + * [person.relationship](https://stripe.com/docs/api/tokens/create_person#create_person_token-person-relationship) + */ + @Parcelize + data class Relationship( + /** + * Whether the person is a director of the account’s legal entity. Currently only required + * for accounts in the EU. Directors are typically members of the governing board of the + * company, or responsible for ensuring the company meets its regulatory obligations. + * + * [person.relationship.directory](https://stripe.com/docs/api/tokens/create_person#create_person_token-person-relationship-directory) + */ + val director: Boolean? = null, + + /** + * Whether the person has significant responsibility to control, manage, or direct the + * organization. + * + * [person.relationship.executive](https://stripe.com/docs/api/tokens/create_person#create_person_token-person-relationship-executive) + */ + val executive: Boolean? = null, + + /** + * Whether the person is an owner of the account’s legal entity. + * + * [person.relationship.owner](https://stripe.com/docs/api/tokens/create_person#create_person_token-person-relationship-owner) + */ + val owner: Boolean? = null, + + /** + * The percent owned by the person of the account’s legal entity. + * + * [person.relationship.representative](https://stripe.com/docs/api/tokens/create_person#create_person_token-person-relationship-representative) + */ + val percentOwnership: Int? = null, + + /** + * Whether the person is authorized as the primary representative of the account. This is + * the person nominated by the business to provide information about themselves, and general + * information about the account. There can only be one representative at any given time. + * At the time the account is created, this person should be set to the person responsible + * for opening the account. + * + * [person.relationship.percent_ownership](https://stripe.com/docs/api/tokens/create_person#create_person_token-person-relationship-percent_ownership) + */ + val representative: Boolean? = null, + + /** + * The person’s title (e.g., CEO, Support Engineer). + * + * [person.relationship.title](https://stripe.com/docs/api/tokens/create_person#create_person_token-person-relationship-title) + */ + val title: String? = null + ) : StripeParamsModel, Parcelable { + override fun toParamMap(): Map { + return listOf( + PARAM_DIRECTOR to director, + PARAM_EXECUTIVE to executive, + PARAM_OWNER to owner, + PARAM_PERCENT_OWNERSHIP to percentOwnership, + PARAM_REPRESENTATIVE to representative, + PARAM_TITLE to title + ).fold(emptyMap()) { acc, (key, value) -> + acc.plus( + value?.let { mapOf(key to it) }.orEmpty() + ) + } + } + + class Builder : ObjectBuilder { + private var director: Boolean? = null + private var executive: Boolean? = null + private var owner: Boolean? = null + private var percentOwnership: Int? = null + private var representative: Boolean? = null + private var title: String? = null + + fun setDirector(director: Boolean?): Builder = apply { + this.director = director + } + + fun setExecutive(executive: Boolean?): Builder = apply { + this.executive = executive + } + + fun setOwner(owner: Boolean?): Builder = apply { + this.owner = owner + } + + fun setPercentOwnership(percentOwnership: Int?): Builder = apply { + this.percentOwnership = percentOwnership + } + + fun setRepresentative(representative: Boolean?): Builder = apply { + this.representative = representative + } + + fun setTitle(title: String?): Builder = apply { + this.title = title + } + + override fun build(): Relationship { + return Relationship( + director = director, + executive = executive, + owner = owner, + percentOwnership = percentOwnership, + representative = representative, + title = title + ) + } + } + + private companion object { + private const val PARAM_DIRECTOR = "director" + private const val PARAM_EXECUTIVE = "executive" + private const val PARAM_OWNER = "owner" + private const val PARAM_PERCENT_OWNERSHIP = "percent_ownership" + private const val PARAM_REPRESENTATIVE = "representative" + private const val PARAM_TITLE = "title" + } + } + + /** + * The person’s verification status. + * + * [person.verification](https://stripe.com/docs/api/tokens/create_person#create_person_token-person-verification) + */ + @Parcelize + data class Verification @JvmOverloads constructor( + /** + * An identifying document, either a passport or local ID card. + * + * [person.verification.document](https://stripe.com/docs/api/tokens/create_person#create_person_token-person-verification-document) + */ + val document: Document? = null, + + /** + * A document showing address, either a passport, local ID card, or utility bill from a well-known utility company. + * + * [person.verification.additional_document](https://stripe.com/docs/api/tokens/create_person#create_person_token-person-verification-additional_document) + */ + val additionalDocument: Document? = null + ) : StripeParamsModel, Parcelable { + override fun toParamMap(): Map { + return listOf( + PARAM_ADDITIONAL_DOCUMENT to document?.toParamMap(), + PARAM_DOCUMENT to additionalDocument?.toParamMap() + ).fold(emptyMap()) { acc, (key, value) -> + acc.plus( + value?.let { mapOf(key to it) }.orEmpty() + ) + } + } + + private companion object { + private const val PARAM_ADDITIONAL_DOCUMENT = "additional_document" + private const val PARAM_DOCUMENT = "document" + } + } + + @Parcelize + data class Document @JvmOverloads constructor( + /** + * The front of an ID returned by a + * [file upload](https://stripe.com/docs/api/tokens/create_person#create_file) with a + * `purpose` value of `identity_document`. The uploaded file needs to be a color image + * (smaller than 8,000px by 8,000px), in JPG or PNG format, and less than 10 MB in size. + * + * [person.verification.document.front](https://stripe.com/docs/api/tokens/create_person#create_person_token-person-verification-document-front) + */ + val front: String? = null, + + /** + * The back of an ID returned by a + * [file upload](https://stripe.com/docs/api/tokens/create_person#create_file) with a + * `purpose` value of `identity_document`. The uploaded file needs to be a color image + * (smaller than 8,000px by 8,000px), in JPG or PNG format, and less than 10 MB in size. + * + * [person.verification.document.back](https://stripe.com/docs/api/tokens/create_person#create_person_token-person-verification-document-back) + */ + val back: String? = null + ) : StripeParamsModel, Parcelable { + override fun toParamMap(): Map { + return listOf( + PARAM_BACK to back, + PARAM_FRONT to front + ).fold(emptyMap()) { acc, (key, value) -> + acc.plus( + value?.let { mapOf(key to it) }.orEmpty() + ) + } + } + + private companion object { + private const val PARAM_BACK = "back" + private const val PARAM_FRONT = "front" + } + } + + class Builder : ObjectBuilder { + private var address: Address? = null + private var addressKana: AddressJapanParams? = null + private var addressKanji: AddressJapanParams? = null + private var dateOfBirth: DateOfBirth? = null + private var email: String? = null + private var firstName: String? = null + private var firstNameKana: String? = null + private var firstNameKanji: String? = null + private var gender: String? = null + private var idNumber: String? = null + private var lastName: String? = null + private var lastNameKana: String? = null + private var lastNameKanji: String? = null + private var maidenName: String? = null + private var metadata: Map? = null + private var phone: String? = null + private var relationship: Relationship? = null + private var ssnLast4: String? = null + private var verification: Verification? = null + + fun setAddress(address: Address?): Builder = apply { + this.address = address + } + + fun setAddressKana(addressKana: AddressJapanParams?): Builder = apply { + this.addressKana = addressKana + } + + fun setAddressKanji(addressKanji: AddressJapanParams?): Builder = apply { + this.addressKanji = addressKanji + } + + fun setDateOfBirth(dateOfBirth: DateOfBirth?): Builder = apply { + this.dateOfBirth = dateOfBirth + } + + fun setEmail(email: String?): Builder = apply { + this.email = email + } + + fun setFirstName(firstName: String?): Builder = apply { + this.firstName = firstName + } + + fun setFirstNameKana(firstNameKana: String?): Builder = apply { + this.firstNameKana = firstNameKana + } + + fun setFirstNameKanji(firstNameKanji: String?): Builder = apply { + this.firstNameKanji = firstNameKanji + } + + fun setGender(gender: String?): Builder = apply { + this.gender = gender + } + + fun setIdNumber(idNumber: String?): Builder = apply { + this.idNumber = idNumber + } + + fun setLastName(lastName: String?): Builder = apply { + this.lastName = lastName + } + + fun setLastNameKana(lastNameKana: String?): Builder = apply { + this.lastNameKana = lastNameKana + } + + fun setLastNameKanji(lastNameKanji: String?): Builder = apply { + this.lastNameKanji = lastNameKanji + } + + fun setMaidenName(maidenName: String?): Builder = apply { + this.maidenName = maidenName + } + + fun setMetadata(metadata: Map?): Builder = apply { + this.metadata = metadata + } + + fun setPhone(phone: String?): Builder = apply { + this.phone = phone + } + + fun setRelationship(relationship: Relationship?): Builder = apply { + this.relationship = relationship + } + + fun setSsnLast4(ssnLast4: String?): Builder = apply { + this.ssnLast4 = ssnLast4 + } + + fun setVerification(verification: Verification?): Builder = apply { + this.verification = verification + } + + override fun build(): PersonTokenParams { + return PersonTokenParams( + address = address, + addressKana = addressKana, + addressKanji = addressKanji, + dateOfBirth = dateOfBirth, + email = email, + firstName = firstName, + firstNameKana = firstNameKana, + firstNameKanji = firstNameKanji, + gender = gender, + idNumber = idNumber, + lastName = lastName, + lastNameKana = lastNameKana, + lastNameKanji = lastNameKanji, + maidenName = maidenName, + metadata = metadata, + phone = phone, + relationship = relationship, + ssnLast4 = ssnLast4, + verification = verification + ) + } + } + + private companion object { + // top level param + private const val PARAM_PERSON = "person" + + private const val PARAM_ADDRESS = "address" + private const val PARAM_ADDRESS_KANA = "address_kana" + private const val PARAM_ADDRESS_KANJI = "address_kanji" + private const val PARAM_DOB = "dob" + private const val PARAM_EMAIL = "email" + private const val PARAM_FIRST_NAME = "first_name" + private const val PARAM_FIRST_NAME_KANA = "first_name_kana" + private const val PARAM_FIRST_NAME_KANJI = "first_name_kanji" + private const val PARAM_GENDER = "gender" + private const val PARAM_ID_NUMBER = "id_number" + private const val PARAM_LAST_NAME = "last_name" + private const val PARAM_LAST_NAME_KANA = "last_name_kana" + private const val PARAM_LAST_NAME_KANJI = "last_name_kanji" + private const val PARAM_MAIDEN_NAME = "maiden_name" + private const val PARAM_METADATA = "metadata" + private const val PARAM_PHONE = "phone" + private const val PARAM_RELATIONSHIP = "relationship" + private const val PARAM_SSN_LAST_4 = "ssn_last_4" + private const val PARAM_VERIFICATION = "verification" + } +} diff --git a/stripe/src/main/java/com/stripe/android/model/Token.kt b/stripe/src/main/java/com/stripe/android/model/Token.kt index 701a5afb4bb..44e0d9d2d1f 100644 --- a/stripe/src/main/java/com/stripe/android/model/Token.kt +++ b/stripe/src/main/java/com/stripe/android/model/Token.kt @@ -55,7 +55,7 @@ data class Token internal constructor( ) : StripeModel, StripePaymentSource { @Retention(AnnotationRetention.SOURCE) @StringDef(TokenType.CARD, TokenType.BANK_ACCOUNT, TokenType.PII, TokenType.ACCOUNT, - TokenType.CVC_UPDATE) + TokenType.CVC_UPDATE, TokenType.PERSON) annotation class TokenType { companion object { const val CARD: String = "card" @@ -63,6 +63,7 @@ data class Token internal constructor( const val PII: String = "pii" const val ACCOUNT: String = "account" const val CVC_UPDATE: String = "cvc_update" + const val PERSON: String = "person" } } diff --git a/stripe/src/main/java/com/stripe/android/model/parsers/TokenJsonParser.kt b/stripe/src/main/java/com/stripe/android/model/parsers/TokenJsonParser.kt index 888f3d5f7b9..f7a45b38535 100644 --- a/stripe/src/main/java/com/stripe/android/model/parsers/TokenJsonParser.kt +++ b/stripe/src/main/java/com/stripe/android/model/parsers/TokenJsonParser.kt @@ -44,7 +44,7 @@ internal class TokenJsonParser : ModelJsonParser { } } else if ( TokenType.PII == tokenType || TokenType.ACCOUNT == tokenType || - TokenType.CVC_UPDATE == tokenType + TokenType.CVC_UPDATE == tokenType || TokenType.PERSON == tokenType ) { Token( id = tokenId, @@ -85,6 +85,7 @@ internal class TokenJsonParser : ModelJsonParser { TokenType.PII -> TokenType.PII TokenType.ACCOUNT -> TokenType.ACCOUNT TokenType.CVC_UPDATE -> TokenType.CVC_UPDATE + TokenType.PERSON -> TokenType.PERSON else -> null } } diff --git a/stripe/src/test/java/com/stripe/android/StripeTest.java b/stripe/src/test/java/com/stripe/android/StripeTest.java index e448ffa3da9..c38c758c20f 100644 --- a/stripe/src/test/java/com/stripe/android/StripeTest.java +++ b/stripe/src/test/java/com/stripe/android/StripeTest.java @@ -18,6 +18,7 @@ import com.stripe.android.model.PaymentMethod; import com.stripe.android.model.PaymentMethodCreateParams; import com.stripe.android.model.PaymentMethodCreateParamsFixtures; +import com.stripe.android.model.PersonTokenParamsFixtures; import com.stripe.android.model.Source; import com.stripe.android.model.SourceCardData; import com.stripe.android.model.SourceParams; @@ -30,6 +31,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.UUID; import org.junit.Before; @@ -946,6 +948,24 @@ public void createTokenSynchronous_withValidCvc_passesIntegrationTest() assertTrue(token.getId().startsWith("cvctok_")); } + @Test + public void testCreatePersonToken() { + final Stripe stripe = createStripe(MainScope()); + stripe.createPersonToken(PersonTokenParamsFixtures.PARAMS, tokenCallback); + verify(tokenCallback).onSuccess(tokenArgumentCaptor.capture()); + final Token token = tokenArgumentCaptor.getValue(); + assertEquals(Token.TokenType.PERSON, Objects.requireNonNull(token).getType()); + assertTrue(token.getId().startsWith("cpt_")); + } + + @Test + public void testCreatePersonTokenSynchronous() throws StripeException { + final Stripe stripe = createStripe(); + final Token token = stripe.createPersonTokenSynchronous(PersonTokenParamsFixtures.PARAMS); + assertEquals(Token.TokenType.PERSON, Objects.requireNonNull(token).getType()); + assertTrue(token.getId().startsWith("cpt_")); + } + @Test public void createAccountTokenSynchronous_withIndividualEntity_passesIntegrationTest() throws StripeException { diff --git a/stripe/src/test/java/com/stripe/android/model/PersonTokenParamsFixtures.java b/stripe/src/test/java/com/stripe/android/model/PersonTokenParamsFixtures.java new file mode 100644 index 00000000000..31a961e4eb6 --- /dev/null +++ b/stripe/src/test/java/com/stripe/android/model/PersonTokenParamsFixtures.java @@ -0,0 +1,39 @@ +package com.stripe.android.model; + +public final class PersonTokenParamsFixtures { + + public static final PersonTokenParams PARAMS = new PersonTokenParams.Builder() + .setFirstName("Jenny") + .setLastName("Rosen") + .setDateOfBirth( + new DateOfBirth(1, 1, 1993) + ) + .setGender("female") + .setAddress( + new Address.Builder() + .setLine1("123 Market St") + .setCity("San Francisco") + .setState("CA") + .setPostalCode("94107") + .setCountry("US") + .build() + ) + .setEmail("jenny@example.com") + .setPhone("1-800-456-7890") + .setSsnLast4("1234") + .setRelationship( + new PersonTokenParams.Relationship.Builder() + .setDirector(true) + .setExecutive(true) + .setOwner(true) + .setPercentOwnership(95) + .build() + ) + .setVerification( + new PersonTokenParams.Verification( + new PersonTokenParams.Document(), + new PersonTokenParams.Document() + ) + ) + .build(); +} From 897a45c7b611fc1c7887d32b2cadc56b2b787a77 Mon Sep 17 00:00:00 2001 From: Michael Shafrir Date: Thu, 9 Jan 2020 13:08:01 -0500 Subject: [PATCH 2/2] Update comments --- stripe/src/main/java/com/stripe/android/Stripe.kt | 6 ------ .../main/java/com/stripe/android/model/PersonTokenParams.kt | 3 --- 2 files changed, 9 deletions(-) diff --git a/stripe/src/main/java/com/stripe/android/Stripe.kt b/stripe/src/main/java/com/stripe/android/Stripe.kt index 1fba363958b..8136cbe032d 100644 --- a/stripe/src/main/java/com/stripe/android/Stripe.kt +++ b/stripe/src/main/java/com/stripe/android/Stripe.kt @@ -1161,9 +1161,6 @@ class Stripe internal constructor( * updating persons associated with a Connect account. * See [the documentation](https://stripe.com/docs/connect/account-tokens) to learn more. * - * Person tokens may be created only in live mode, with your application’s publishable key. - * Your application’s secret key may be used to create person tokens only in test mode. - * * See [Create a person token](https://stripe.com/docs/api/tokens/create_person) * * @param params the person token creation params @@ -1190,9 +1187,6 @@ class Stripe internal constructor( * updating persons associated with a Connect account. * See [the documentation](https://stripe.com/docs/connect/account-tokens) to learn more. * - * Person tokens may be created only in live mode, with your application’s publishable key. - * Your application’s secret key may be used to create person tokens only in test mode. - * * See [Create a person token](https://stripe.com/docs/api/tokens/create_person) * * @param params the person token creation params diff --git a/stripe/src/main/java/com/stripe/android/model/PersonTokenParams.kt b/stripe/src/main/java/com/stripe/android/model/PersonTokenParams.kt index eb7ef132e0c..dafac703f6f 100644 --- a/stripe/src/main/java/com/stripe/android/model/PersonTokenParams.kt +++ b/stripe/src/main/java/com/stripe/android/model/PersonTokenParams.kt @@ -9,9 +9,6 @@ import kotlinx.android.parcel.Parcelize * updating persons associated with a Connect account. * See [the documentation](https://stripe.com/docs/connect/account-tokens) to learn more. * - * Person tokens may be created only in live mode, with your application’s publishable key. - * Your application’s secret key may be used to create person tokens only in test mode. - * * See [Create a person token](https://stripe.com/docs/api/tokens/create_person) */ @Parcelize