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 cancellation_reason attribute to PaymentIntent #1449

Merged
merged 1 commit into from
Aug 29, 2019
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
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ public final class PaymentIntent extends StripeModel implements StripeIntent {
private static final String FIELD_OBJECT = "object";
private static final String FIELD_AMOUNT = "amount";
private static final String FIELD_CREATED = "created";
private static final String FIELD_CANCELED = "canceled_at";
private static final String FIELD_CANCELED_AT = "canceled_at";
private static final String FIELD_CANCELLATION_REASON = "cancellation_reason";
private static final String FIELD_CAPTURE_METHOD = "capture_method";
private static final String FIELD_CLIENT_SECRET = "client_secret";
private static final String FIELD_CONFIRMATION_METHOD = "confirmation_method";
Expand All @@ -58,6 +59,7 @@ public final class PaymentIntent extends StripeModel implements StripeIntent {
@NonNull private final List<String> mPaymentMethodTypes;
@Nullable private final Long mAmount;
private final long mCanceledAt;
@Nullable private final CancellationReason mCancellationReason;
@Nullable private final String mCaptureMethod;
@Nullable private final String mClientSecret;
@Nullable private final String mConfirmationMethod;
Expand Down Expand Up @@ -108,6 +110,14 @@ public long getCanceledAt() {
return mCanceledAt;
}

/**
* @return Reason for cancellation of this PaymentIntent
*/
@Nullable
public CancellationReason getCancellationReason() {
return mCancellationReason;
}

/**
* @return One of <code>automatic</code> (default) or <code>manual</code>.
*
Expand Down Expand Up @@ -311,6 +321,7 @@ private PaymentIntent(
@NonNull List<String> paymentMethodTypes,
@Nullable Long amount,
long canceledAt,
@Nullable CancellationReason cancellationReason,
@Nullable String captureMethod,
@Nullable String clientSecret,
@Nullable String confirmationMethod,
Expand All @@ -330,6 +341,7 @@ private PaymentIntent(
mPaymentMethodTypes = paymentMethodTypes;
mAmount = amount;
mCanceledAt = canceledAt;
mCancellationReason = cancellationReason;
mCaptureMethod = captureMethod;
mClientSecret = clientSecret;
mConfirmationMethod = confirmationMethod;
Expand Down Expand Up @@ -374,7 +386,9 @@ public static PaymentIntent fromJson(@Nullable JSONObject jsonObject) {
final List<String> paymentMethodTypes = jsonArrayToList(
jsonObject.optJSONArray(FIELD_PAYMENT_METHOD_TYPES));
final Long amount = optLong(jsonObject, FIELD_AMOUNT);
final Long canceledAt = jsonObject.optLong(FIELD_CANCELED);
final long canceledAt = jsonObject.optLong(FIELD_CANCELED_AT);
final CancellationReason cancellationReason =
CancellationReason.fromCode(optString(jsonObject, FIELD_CANCELLATION_REASON));
final String captureMethod = optString(jsonObject, FIELD_CAPTURE_METHOD);
final String clientSecret = optString(jsonObject, FIELD_CLIENT_SECRET);
final String confirmationMethod = optString(jsonObject, FIELD_CONFIRMATION_METHOD);
Expand All @@ -398,6 +412,7 @@ public static PaymentIntent fromJson(@Nullable JSONObject jsonObject) {
paymentMethodTypes,
amount,
canceledAt,
cancellationReason,
captureMethod,
clientSecret,
confirmationMethod,
Expand Down Expand Up @@ -425,6 +440,7 @@ private boolean typedEquals(@NonNull PaymentIntent paymentIntent) {
&& ObjectUtils.equals(mObjectType, paymentIntent.mObjectType)
&& ObjectUtils.equals(mAmount, paymentIntent.mAmount)
&& ObjectUtils.equals(mCanceledAt, paymentIntent.mCanceledAt)
&& ObjectUtils.equals(mCancellationReason, paymentIntent.mCancellationReason)
&& ObjectUtils.equals(mCaptureMethod, paymentIntent.mCaptureMethod)
&& ObjectUtils.equals(mClientSecret, paymentIntent.mClientSecret)
&& ObjectUtils.equals(mConfirmationMethod, paymentIntent.mConfirmationMethod)
Expand All @@ -445,10 +461,11 @@ private boolean typedEquals(@NonNull PaymentIntent paymentIntent) {

@Override
public int hashCode() {
return ObjectUtils.hash(mId, mObjectType, mAmount, mCanceledAt, mCaptureMethod,
mClientSecret, mConfirmationMethod, mCreated, mCurrency, mDescription, mLiveMode,
mReceiptEmail, mSource, mStatus, mPaymentMethodTypes, mNextAction, mNextActionType,
mPaymentMethodId, mSetupFutureUsage, mLastPaymentError);
return ObjectUtils.hash(mId, mObjectType, mAmount, mCanceledAt, mCancellationReason,
mCaptureMethod, mClientSecret, mConfirmationMethod, mCreated, mCurrency,
mDescription, mLiveMode, mReceiptEmail, mSource, mStatus, mPaymentMethodTypes,
mNextAction, mNextActionType, mPaymentMethodId, mSetupFutureUsage,
mLastPaymentError);
}

/**
Expand Down Expand Up @@ -657,4 +674,31 @@ private static Type fromCode(@Nullable String typeCode) {
}
}
}

enum CancellationReason {
Duplicate("duplicate"),
Fraudulent("fraudulent"),
RequestedByCustomer("requested_by_customer"),
Abandoned("abandoned"),
FailedInvoice("failed_invoice"),
VoidInvoice("void_invoice"),
Automatic("automatic");

@NonNull private final String code;

CancellationReason(@NonNull String code) {
this.code = code;
}

@Nullable
static CancellationReason fromCode(@Nullable String code) {
for (CancellationReason cancellationReason : values()) {
if (cancellationReason.code.equals(code)) {
return cancellationReason;
}
}

return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,74 @@ public final class PaymentIntentFixtures {
"\t\"status\": \"requires_payment_method\"\n" +
"}"));

public static final PaymentIntent CANCELLED = Objects.requireNonNull(PaymentIntent.fromString(
"{\n" +
" \"id\": \"pi_1FCpMECRMbs6FrXfVulorSf5\",\n" +
" \"object\": \"payment_intent\",\n" +
" \"amount\": 4000,\n" +
" \"amount_capturable\": 0,\n" +
" \"amount_received\": 0,\n" +
" \"application\": null,\n" +
" \"application_fee_amount\": null,\n" +
" \"canceled_at\": 1567091866,\n" +
" \"cancellation_reason\": \"abandoned\",\n" +
" \"capture_method\": \"automatic\",\n" +
" \"charges\": {\n" +
" \"object\": \"list\",\n" +
" \"data\": [],\n" +
" \"has_more\": false,\n" +
" \"total_count\": 0,\n" +
" \"url\": \"/v1/charges?payment_intent=pi_1FCpMECRMbs6FrXfVulorSf5\"\n" +
" },\n" +
" \"client_secret\": \"pi_1FCpMECRMbs6FrXfVulorSf5_secret_oSppt5A\",\n" +
" \"confirmation_method\": \"manual\",\n" +
" \"created\": 1567091778,\n" +
" \"currency\": \"usd\",\n" +
" \"customer\": \"cus_FWhpaTLIPWLhpJ\",\n" +
" \"description\": \"Example PaymentIntent\",\n" +
" \"invoice\": null,\n" +
" \"last_payment_error\": null,\n" +
" \"livemode\": false,\n" +
" \"metadata\": {\n" +
" \"order_id\": \"5278735C-1F40-407D-933A-286E463E72D8\"\n" +
" },\n" +
" \"next_action\": null,\n" +
" \"on_behalf_of\": null,\n" +
" \"payment_method\": null,\n" +
" \"payment_method_options\": {\n" +
" \"card\": {\n" +
" \"request_three_d_secure\": \"automatic\"\n" +
" }\n" +
" },\n" +
" \"payment_method_types\": [\n" +
" \"card\"\n" +
" ],\n" +
" \"receipt_email\": null,\n" +
" \"review\": null,\n" +
" \"setup_future_usage\": null,\n" +
" \"shipping\": {\n" +
" \"address\": {\n" +
" \"city\": \"San Francisco\",\n" +
" \"country\": \"US\",\n" +
" \"line1\": \"123 Market St\",\n" +
" \"line2\": \"#345\",\n" +
" \"postal_code\": \"94107\",\n" +
" \"state\": \"CA\"\n" +
" },\n" +
" \"carrier\": null,\n" +
" \"name\": \"Fake Name\",\n" +
" \"phone\": \"(555) 555-5555\",\n" +
" \"tracking_number\": null\n" +
" },\n" +
" \"source\": null,\n" +
" \"statement_descriptor\": null,\n" +
" \"statement_descriptor_suffix\": null,\n" +
" \"status\": \"canceled\",\n" +
" \"transfer_data\": null,\n" +
" \"transfer_group\": null\n" +
"}"
));

public static final PaymentIntent.RedirectData REDIRECT_DATA =
new PaymentIntent.RedirectData("https://example.com",
"yourapp://post-authentication-return-url");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,6 @@
@RunWith(RobolectricTestRunner.class)
public class PaymentIntentTest {

private static final String PAYMENT_INTENT_WITH_SOURCE_JSON = "{\n" +
" \"id\": \"pi_1CkiBMLENEVhOs7YMtUehLau\",\n" +
" \"object\": \"payment_intent\",\n" +
" \"payment_method_types\": [\n" +
" \"card\"\n" +
" ],\n" +
" \"amount\": 1000,\n" +
" \"canceled_at\": 1530839340,\n" +
" \"capture_method\": \"automatic\",\n" +
" \"client_secret\": \"pi_1CkiBMLENEVhOs7YMtUehLau_secret_s4O8SDh7s6spSmHDw1VaYPGZA\",\n" +
" \"confirmation_method\": \"publishable\",\n" +
" \"created\": 1530838340,\n" +
" \"currency\": \"usd\",\n" +
" \"description\": \"Example PaymentIntent charge\",\n" +
" \"livemode\": false,\n" +
" \"next_action\": null,\n" +
" \"receipt_email\": null,\n" +
" \"shipping\": null,\n" +
" \"source\": \"src_1CkiC3LENEVhOs7YMSa4yx4G\",\n" +
" \"status\": \"succeeded\"\n" +
"}\n";

private static final String BAD_URL = "nonsense-blahblah";

private static final String PAYMENT_INTENT_WITH_SOURCE_WITH_BAD_AUTH_URL_JSON = "{\n" +
Expand Down Expand Up @@ -63,64 +41,6 @@ public class PaymentIntentTest {
" \"status\": \"requires_action\"\n" +
"}\n";

private static final String PAYMENT_INTENT_WITH_PAYMENT_METHODS_JSON = "{\n" +
" \"id\": \"pi_Aabcxyz01aDfoo\",\n" +
" \"object\": \"payment_intent\",\n" +
" \"amount\": 750,\n" +
" \"amount_capturable\": 0,\n" +
" \"amount_received\": 750,\n" +
" \"application\": null,\n" +
" \"application_fee_amount\": null,\n" +
" \"canceled_at\": null,\n" +
" \"cancellation_reason\": null,\n" +
" \"capture_method\": \"automatic\",\n" +
" \"charges\": {\n" +
" \"object\": \"list\",\n" +
" \"data\": [],\n" +
" \"has_more\": false,\n" +
" \"total_count\": 0,\n" +
" \"url\": \"/v1/charges?payment_intent=pi_Aabcxyz01aDfoo\"\n" +
" },\n" +
" \"client_secret\": null,\n" +
" \"confirmation_method\": \"publishable\",\n" +
" \"created\": 123456789,\n" +
" \"currency\": \"usd\",\n" +
" \"customer\": null,\n" +
" \"description\": \"PaymentIntent Description\",\n" +
" \"last_payment_error\": null,\n" +
" \"livemode\": false,\n" +
" \"metadata\": {\n" +
" \"order_id\": \"123456789\"\n" +
" },\n" +
" \"next_action\": null,\n" +
" \"on_behalf_of\": null,\n" +
" \"payment_method\": null,\n" +
" \"payment_method_types\": [\n" +
" \"card\"\n" +
" ],\n" +
" \"receipt_email\": \"jenny@example.com\",\n" +
" \"review\": null,\n" +
" \"shipping\": {\n" +
" \"address\": {\n" +
" \"city\": \"Stockholm\",\n" +
" \"country\": \"Sweden\",\n" +
" \"line1\": \"Mega street 5\",\n" +
" \"line2\": \"Mega street 5\",\n" +
" \"postal_code\": \"12233JJHH\",\n" +
" \"state\": \"NYC\"\n" +
" },\n" +
" \"carrier\": null,\n" +
" \"name\": \"Mohit Name\",\n" +
" \"phone\": null,\n" +
" \"tracking_number\": null\n" +
" },\n" +
" \"source\": \"src_1E884r2eZvKYlo2CTft0qEyY\",\n" +
" \"statement_descriptor\": \"PaymentIntent Statement Descriptor\",\n" +
" \"status\": \"succeeded\",\n" +
" \"transfer_data\": null,\n" +
" \"transfer_group\": null\n" +
"}";

private static final String PARTIAL_PAYMENT_INTENT_WITH_REDIRECT_URL_JSON = "{\n" +
"\t\"id\": \"pi_Aabcxyz01aDfoo\",\n" +
"\t\"object\": \"payment_intent\",\n" +
Expand Down Expand Up @@ -198,6 +118,7 @@ public void parsePaymentIntentWithPaymentMethods() {
assertEquals("manual", paymentIntent.getConfirmationMethod());
assertNotNull(paymentIntent.getNextAction());
assertEquals("jenny@example.com", paymentIntent.getReceiptEmail());
assertNull(paymentIntent.getCancellationReason());
}

@Test
Expand Down Expand Up @@ -246,4 +167,12 @@ public void getLastPaymentError_parsesCorrectly() {
lastPaymentError.message
);
}

@Test
public void testCanceled() {
assertEquals(PaymentIntent.CancellationReason.Abandoned,
PaymentIntentFixtures.CANCELLED.getCancellationReason());
assertEquals(1567091866L,
PaymentIntentFixtures.CANCELLED.getCanceledAt());
}
}