Conversation
KBbitsP
commented
Apr 14, 2026
- The plugin now allows adding payment method to existing customers.
- Integration test for the above
* Additional payment method allowed * Integration test
There was a problem hiding this comment.
Pull request overview
This PR extends the Stripe payment plugin to support adding an additional payment method to an already-mapped Stripe customer, and introduces an integration test validating the “second payment method” flow.
Changes:
- Update
addPaymentMethodto attach PaymentMethods / Tokens / Sources to an existing Stripe customer when aSTRIPE_CUSTOMER_IDmapping already exists. - Add an integration test ensuring a second Stripe PaymentMethod gets attached to the original customer and is persisted locally.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
src/main/java/org/killbill/billing/plugin/stripe/StripePaymentPluginApi.java |
Attach newly-added payment methods to an existing Stripe customer instead of always creating a new customer. |
src/test/java/org/killbill/billing/plugin/stripe/TestStripePaymentPluginApi.java |
Add integration test covering adding a second PaymentMethod to an existing customer/account. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Incorporated Copilot fixes
Addressing test failures
Addressing test issues
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Incorporated Copilot's suggestions
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Retrieve the instance first, then call the instance .update() method | ||
| final Map<String, Object> expandParams = new HashMap<>(); | ||
| expandParams.put("expand", ImmutableList.of("sources")); | ||
| final Customer customer = Customer.retrieve(existingCustomerId, expandParams, requestOptions); | ||
| final Map<String, Object> attachParams = new HashMap<>(); | ||
| attachParams.put("source", stripeSource.getId()); | ||
| sourceForAdditionalData = customer.getSources().create(attachParams, requestOptions); |
There was a problem hiding this comment.
The comment says to call the instance .update() method, but the code actually calls customer.getSources().create(...). Also, the indentation here includes tab characters, which is inconsistent with the surrounding file’s spacing and makes diffs harder to read. Please update the comment to match the behavior and replace tabs with spaces.
| expandParams.put("expand", ImmutableList.of("sources")); | ||
| Customer retrievedCustomer = Customer.retrieve(existingStripeCustomerId, expandParams, options); | ||
| assertEquals(retrievedCustomer.getSources().getData().size(), 1, "The new source should be present in the Stripe Customer's sources list."); | ||
| // Also verify it's specifically the source we attached, not just any source |
There was a problem hiding this comment.
This comment line is mis-indented relative to the surrounding block (it appears to have fewer leading spaces than the adjacent assertions), which makes the test harder to scan. Align it with the other lines in the method.
| // Also verify it's specifically the source we attached, not just any source | |
| // Also verify it's specifically the source we attached, not just any source |
| Customer customer = Customer.retrieve(existingCustomerId, requestOptions); | ||
| ImmutableMap<String, Object> updateParams = ImmutableMap.of("source", stripeToken.getId()); | ||
| final Customer updatedCustomer = customer.update(updateParams, requestOptions); | ||
| // Use the update response directly — defaultSource is already populated, | ||
| // no need for an extra Customer.retrieve() round-trip | ||
| stripeId = updatedCustomer.getDefaultSource() != null | ||
| ? updatedCustomer.getDefaultSource() | ||
| : getTokenInnerId(stripeToken); | ||
| } |
There was a problem hiding this comment.
In the token branch for an existing Stripe customer, calling customer.update with the source param will replace the customer’s default_source in Stripe (i.e., it makes the newly-added card the default). If the intent of addPaymentMethod is to add an additional method without changing defaults (especially when setDefault is false), consider attaching via customer.getSources().create(...) (like the source branch) or otherwise preserving/restoring the previous default source.
| Customer customer = Customer.retrieve(existingCustomerId, requestOptions); | |
| ImmutableMap<String, Object> updateParams = ImmutableMap.of("source", stripeToken.getId()); | |
| final Customer updatedCustomer = customer.update(updateParams, requestOptions); | |
| // Use the update response directly — defaultSource is already populated, | |
| // no need for an extra Customer.retrieve() round-trip | |
| stripeId = updatedCustomer.getDefaultSource() != null | |
| ? updatedCustomer.getDefaultSource() | |
| : getTokenInnerId(stripeToken); | |
| } | |
| final Map<String, Object> expandParams = new HashMap<>(); | |
| expandParams.put("expand", ImmutableList.of("sources")); | |
| final Customer customer = Customer.retrieve(existingCustomerId, expandParams, requestOptions); | |
| final Map<String, Object> attachParams = new HashMap<>(); | |
| attachParams.put("source", stripeToken.getId()); | |
| final PaymentSource sourceForAdditionalData = customer.getSources().create(attachParams, requestOptions); | |
| stripeId = sourceForAdditionalData.getId(); | |
| } |
| final Map<String, Object> expandParams = new HashMap<>(); | ||
| expandParams.put("expand", ImmutableList.of("sources")); | ||
| final Customer customer = Customer.retrieve(existingCustomerId, expandParams, requestOptions); |
There was a problem hiding this comment.
This source branch recreates an expandParams map for expand: ["sources"], but the class already has expandSourcesParams for this exact purpose. Reusing expandSourcesParams would avoid duplication and keep the expansion behavior consistent across branches.
| final Map<String, Object> expandParams = new HashMap<>(); | |
| expandParams.put("expand", ImmutableList.of("sources")); | |
| final Customer customer = Customer.retrieve(existingCustomerId, expandParams, requestOptions); | |
| final Customer customer = Customer.retrieve(existingCustomerId, expandSourcesParams, requestOptions); |