Skip to content
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
14 changes: 12 additions & 2 deletions src/main/java/com/stripe/mpp/methods/stripe/StripeApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.stripe.StripeClient;
import com.stripe.exception.StripeException;
import com.stripe.model.PaymentIntent;
import com.stripe.net.RequestOptions;
import com.stripe.param.PaymentIntentCreateParams;
import com.stripe.mpp.error.VerificationFailedException;

Expand Down Expand Up @@ -54,7 +55,8 @@ Result createAndConfirm(
String currency,
String spt,
List<String> paymentMethodTypes,
Map<String, String> metadata
Map<String, String> metadata,
String challengeId
) {
try {
StripeClient client = new StripeClient(secretKey);
Expand All @@ -70,11 +72,19 @@ Result createAndConfirm(
builder.putAllMetadata(metadata);
}

PaymentIntent pi = client.paymentIntents().create(builder.build());
RequestOptions options = RequestOptions.builder()
.setIdempotencyKey(buildIdempotencyKey(challengeId, spt))
.build();

PaymentIntent pi = client.paymentIntents().create(builder.build(), options);
return new Result(pi.getId(), pi.getStatus());

} catch (StripeException e) {
throw new VerificationFailedException(e.getMessage());
}
}

static String buildIdempotencyKey(String challengeId, String spt) {
return "mpp-java_" + challengeId + "_" + spt;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,9 @@ public Receipt verify(Credential credential, Map<String, Object> request) {
Map<String, Object> methodDetails = methodDetails(request);
List<String> paymentMethodTypes = paymentMethodTypes(methodDetails);
Map<String, String> metadata = extractMetadata(methodDetails);
String challengeId = credential.challenge().id();
StripeApi.Result result = stripeApi.createAndConfirm(
secretKey, amountMinorUnits, currency, spt, paymentMethodTypes, metadata);
secretKey, amountMinorUnits, currency, spt, paymentMethodTypes, metadata, challengeId);

if ("requires_action".equals(result.status())) {
throw new com.stripe.mpp.error.PaymentActionRequiredException(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ static class StubStripeApi extends StripeApi {
String lastCurrency;
List<String> lastPaymentMethodTypes;
Map<String, String> lastMetadata;
String lastChallengeId;

StubStripeApi(StripeApi.Result result) {
this.result = result;
Expand All @@ -66,13 +67,15 @@ static class StubStripeApi extends StripeApi {
@Override
StripeApi.Result createAndConfirm(
String secretKey, long amountMinorUnits, String currency,
String spt, List<String> paymentMethodTypes, Map<String, String> metadata
String spt, List<String> paymentMethodTypes, Map<String, String> metadata,
String challengeId
) {
this.lastSpt = spt;
this.lastAmount = amountMinorUnits;
this.lastCurrency = currency;
this.lastPaymentMethodTypes = paymentMethodTypes;
this.lastMetadata = metadata;
this.lastChallengeId = challengeId;
return result;
}
}
Expand Down Expand Up @@ -188,7 +191,8 @@ void stripeApiExceptionBecomesVerificationFailed() {
@Override
StripeApi.Result createAndConfirm(
String secretKey, long amountMinorUnits, String currency,
String spt, List<String> paymentMethodTypes, Map<String, String> metadata
String spt, List<String> paymentMethodTypes, Map<String, String> metadata,
String challengeId
) {
throw new VerificationFailedException("card_declined");
}
Expand All @@ -208,6 +212,41 @@ void metadataPassedToStripeApi() {
assertThat(api.lastMetadata).containsEntry("orderId", "order-42");
}

@Test
void challengeIdPassedForIdempotency() {
StubStripeApi api = new StubStripeApi(new StripeApi.Result("pi_abc123", "succeeded"));
intent(api).verify(credential("spt_xxx"), REQUEST);

assertThat(api.lastChallengeId).isEqualTo("chal-id");
}

@Test
void idempotencyKeyDerivedFromChallengeIdAndSpt() {
String key = StripeApi.buildIdempotencyKey("chal-id", "spt_xxx");
assertThat(key).isEqualTo("mpp-java_chal-id_spt_xxx");
}

@Test
void idempotencyKeyDeterministic() {
String key1 = StripeApi.buildIdempotencyKey("chal-abc", "spt_123");
String key2 = StripeApi.buildIdempotencyKey("chal-abc", "spt_123");
assertThat(key1).isEqualTo(key2);
}

@Test
void idempotencyKeyDiffersForDifferentChallenges() {
String key1 = StripeApi.buildIdempotencyKey("chal-abc", "spt_123");
String key2 = StripeApi.buildIdempotencyKey("chal-def", "spt_123");
assertThat(key1).isNotEqualTo(key2);
}

@Test
void idempotencyKeyDiffersForDifferentSpts() {
String key1 = StripeApi.buildIdempotencyKey("chal-abc", "spt_123");
String key2 = StripeApi.buildIdempotencyKey("chal-abc", "spt_456");
assertThat(key1).isNotEqualTo(key2);
}

@Test
void missingPaymentMethodTypesThrows() {
StubStripeApi api = new StubStripeApi(new StripeApi.Result("pi_abc123", "succeeded"));
Expand Down
Loading