Skip to content

Commit

Permalink
Correctly parse JSON when creating or deleting customer card source (#…
Browse files Browse the repository at this point in the history
…857)

Both `POST /v1/customers/:customer_id/sources` and
`DELETE /v1/customers/:customer_id/sources/:source_id` return
a different shape depending on whether the object referenced by
`:source_id` is a `Source` or a `Card`. The current implementation
was not handling the `Card` case. The impact of this is that the
listener passed to either `CustomerSession#addCustomerSource` or
`CustomerSession#deleteCustomerSource` was not being called when
the source was a `Card`.

The short-term fix is to create a `Source` object and only populate
its `id` field. This is not ideal because we are creating a `Source`
with a card id, but it resolves the issue of the listener not being
called.

The long-term fix is to pass a `StripePaymentSource` to the
listener.

ANDROID-344
  • Loading branch information
mshafrir-stripe authored Apr 9, 2019
1 parent 8e14edd commit f3e65da
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 3 deletions.
32 changes: 29 additions & 3 deletions stripe/src/main/java/com/stripe/android/model/Source.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
public class Source extends StripeJsonModel implements StripePaymentSource {

static final String VALUE_SOURCE = "source";
private static final String VALUE_CARD = "card";

@Retention(RetentionPolicy.SOURCE)
@StringDef({
Expand Down Expand Up @@ -145,6 +146,12 @@ public class Source extends StripeJsonModel implements StripePaymentSource {
@Nullable @SourceType private String mType;
@Nullable @Usage private String mUsage;

private Source(@Nullable String id, @Nullable SourceCardData sourceTypeModel) {
mId = id;
mType = CARD;
mSourceTypeModel = sourceTypeModel;
}

private Source(
@Nullable String id,
@Nullable Long amount,
Expand Down Expand Up @@ -422,10 +429,30 @@ public static Source fromString(@Nullable String jsonString) {

@Nullable
public static Source fromJson(@Nullable JSONObject jsonObject) {
if (jsonObject == null || !VALUE_SOURCE.equals(jsonObject.optString(FIELD_OBJECT))) {
if (jsonObject == null) {
return null;
}

final String objectType = jsonObject.optString(FIELD_OBJECT);
if (VALUE_CARD.equals(objectType)) {
return fromCardJson(jsonObject);
} else if (VALUE_SOURCE.equals(objectType)) {
return fromSourceJson(jsonObject);
} else {
return null;
}
}

@NonNull
private static Source fromCardJson(@NonNull JSONObject jsonObject) {
return new Source(
optString(jsonObject, FIELD_ID),
SourceCardData.fromJson(jsonObject)
);
}

@NonNull
private static Source fromSourceJson(@NonNull JSONObject jsonObject) {
final String id = optString(jsonObject, FIELD_ID);
final Long amount = optLong(jsonObject, FIELD_AMOUNT);
final String clientSecret = optString(jsonObject, FIELD_CLIENT_SECRET);
Expand Down Expand Up @@ -459,8 +486,7 @@ public static Source fromJson(@Nullable JSONObject jsonObject) {
// trying to force it to be a type that we know of.
final Map<String, Object> sourceTypeData =
StripeJsonUtils.jsonObjectToMap(jsonObject.optJSONObject(typeRaw));
final StripeSourceTypeModel sourceTypeModel =
MODELED_TYPES.contains(typeRaw)
final StripeSourceTypeModel sourceTypeModel = MODELED_TYPES.contains(typeRaw)
? optStripeJsonModel(jsonObject, typeRaw, StripeSourceTypeModel.class)
: null;

Expand Down
52 changes: 52 additions & 0 deletions stripe/src/test/java/com/stripe/android/model/SourceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

/**
Expand Down Expand Up @@ -173,6 +174,38 @@ public class SourceTest {
"}" +
"}";

private static final String DELETED_CARD_JSON = "{\n" +
" \"id\": \"card_1ELdAlCRMbs6FrXfNbmZEOb7\",\n" +
" \"object\": \"card\",\n" +
" \"deleted\": true\n" +
"}";

private static final String CREATED_CARD_JSON = "{\n" +
" \"id\": \"card_1ELxrOCRMbs6FrXfdxOGjnaD\",\n" +
" \"object\": \"card\",\n" +
" \"address_city\": null,\n" +
" \"address_country\": null,\n" +
" \"address_line1\": null,\n" +
" \"address_line1_check\": null,\n" +
" \"address_line2\": null,\n" +
" \"address_state\": null,\n" +
" \"address_zip\": null,\n" +
" \"address_zip_check\": null,\n" +
" \"brand\": \"Visa\",\n" +
" \"country\": \"US\",\n" +
" \"customer\": \"cus_Epd7N0VR3cdjsr\",\n" +
" \"cvc_check\": null,\n" +
" \"dynamic_last4\": null,\n" +
" \"exp_month\": 4,\n" +
" \"exp_year\": 2020,\n" +
" \"funding\": \"credit\",\n" +
" \"last4\": \"4242\",\n" +
" \"metadata\": {\n" +
" },\n" +
" \"name\": null,\n" +
" \"tokenization_method\": null\n" +
"}\n";

private Source mSource;

@Before
Expand Down Expand Up @@ -209,4 +242,23 @@ public void fromJsonString_withCustomType_createsSourceWithCustomType() {
assertNull(customSource.getSourceTypeModel());
assertNotNull("Failed to find custom api params", customSource.getSourceTypeData());
}

@Test
public void fromJsonString_withDeletedCardJson_shouldReturnSourceWithCardId() {
final Source source = Source.fromString(DELETED_CARD_JSON);
assertNotNull(source);
assertEquals("card_1ELdAlCRMbs6FrXfNbmZEOb7", source.getId());
}

@Test
public void fromJsonString_withCreatedCardJson_shouldReturnSourceWithCardId() {
final Source source = Source.fromString(CREATED_CARD_JSON);
assertNotNull(source);
assertEquals("card_1ELxrOCRMbs6FrXfdxOGjnaD", source.getId());
assertEquals(Source.CARD, source.getType());
assertTrue(source.getSourceTypeModel() instanceof SourceCardData);

final SourceCardData sourceCardData = (SourceCardData) source.getSourceTypeModel();
assertEquals(Card.VISA, sourceCardData.getBrand());
}
}

0 comments on commit f3e65da

Please sign in to comment.