Skip to content

fix(gocardless): make mandate reuse work under a Customer naming series#232

Open
0spinboson wants to merge 3 commits into
frappe:developfrom
nlvegan:fix/gocardless-mandate-customer-field
Open

fix(gocardless): make mandate reuse work under a Customer naming series#232
0spinboson wants to merge 3 commits into
frappe:developfrom
nlvegan:fix/gocardless-mandate-customer-field

Conversation

@0spinboson

Copy link
Copy Markdown

Why

GoCardless Mandate.customer is a Link -> Customer custom field, so it must
hold the Customer docname. Two code paths instead used the Customer display
name (customer_name):

  • gocardless_confirmation.create_mandate() stored customer_name in the
    mandate's customer field, and GoCardlessSettings.check_mandate_validity()
    looked the mandate up by the same display value.
  • gocardless_checkout.check_mandate() resolved the payer with
    get_doc("Customer", payer_name), where payer_name is the customer_name
    that Payment Request puts in the checkout URL.

On sites where the Customer naming rule is "Customer Name", the docname equals
customer_name, so this works by coincidence. On sites using a Customer naming
series, the docname differs from customer_name, and:

  • the mandate insert fails LinkValidationError and is silently swallowed, so
    no mandate row is stored;
  • even with a row present, the reuse lookup keys on the wrong value;
  • the checkout payer lookup raises DoesNotExistError.

Because the first charge goes through the redirect-flow mandate id directly, the
first payment still succeeds; the breakage only surfaces on the next payment as
a repeated mandate prompt, which is the behaviour reported in
#89.

What

  • Store and look the mandate up by the Customer docname, carried through as
    data["customer"].
  • Extract GoCardlessSettings.get_registered_mandate() for the lookup, which
    also collapses the previous exists() + get_value() double query.
  • Extract gocardless_checkout.get_payer_customer(), which resolves the payer
    by docname and falls back to customer_name, so the redirect flow is created
    regardless of the Customer naming rule.

Verified end-to-end against the GoCardless sandbox API under a Customer naming
series: the mandate is now persisted and reused on the second payment. Tests
cover the mandate keying, the lookup helper, and the payer resolution.

This resolves #89 for sites using a Customer naming series. Sites
named by customer_name were already unaffected.

Note

Builds on #231 (its diagnostic logging commit is included here as the base of
this branch) and should be merged after it; once #231 lands, this PR's diff
reduces to the two fix commits.

Refs #89

When create_mandate() cannot persist a GoCardless Mandate, future payments stop
reusing it and re-prompt the payer (frappe#89). Frappe already captures the traceback,
but the failing transaction is buried in a locals dump. Add a readable summary
line (mandate id, reference document, customer, gocardless_customer) above the
traceback so the Error Log entry is actionable on its own.

Test drives a forced create_mandate() failure and asserts the summary is logged.
No behavioural change.
The GoCardless Mandate.customer field is a Link -> Customer custom field, so it
must hold the Customer docname. create_mandate() stored customer_name (display)
and check_mandate_validity() looked it up by the same display value. Under a
Customer naming series the docname differs from customer_name, so:

  - the insert fails Link validation and is silently swallowed -> no mandate row
  - even if a row existed, the lookup keyed on the wrong value

The first payment still succeeds (it charges via the redirect-flow mandate id
directly), so the breakage only surfaces on the *second* payment as a re-prompt.

Store and look the mandate up by the Customer docname (carried through as
data['customer']). Extract get_registered_mandate() for the lookup, which also
collapses the previous exists()+get_value() double query. Verified end-to-end
against the GoCardless sandbox API under a naming series.

Refs frappe#89
gocardless_checkout.check_mandate() looked the payer up with
get_doc('Customer', payer_name), but Payment Request fills payer_name with
customer_name (the display name). Under a Customer naming series that is not the
Customer docname, so the lookup raises DoesNotExistError and the first
subscription fails before a mandate can be created.

Extract get_payer_customer() to resolve by docname, then fall back to
customer_name, so the redirect flow is created regardless of Customer naming.

Refs frappe#89
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Gocardless payment request issue (broken after update)

1 participant