Conversation
A payment made through a hosted button, link or QR code never touches the site, so the site registers a webhook on the merchant's PayPal account for the PAYMENT.CAPTURE.* events, verifies each delivery with PayPal, and records it as a jetpack_paypal_capture_* Tracks event attributed to the connection owner. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
PayPal delivers webhook events only to the app whose client id processed the payment, and under our FIRST_PARTY referral that is the merchant's own app, so the site is the only place that sees a capture. It now forwards each verified event, minus the payer, to wpcom/v2/paypal/webhook-events as the blog, retrying from cron on an outage, so payments can be logged centrally. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Are you an Automattician? Please test your changes on all WordPress.com environments to help mitigate accidental explosions.
Interested in more tips and information?
|
|
Thank you for your PR! When contributing to Jetpack, we have a few suggestions that can help us test and review your patch:
This comment will be updated as you work on your PR and make changes. If you think that some of those checks are not needed for your PR, please explain why you think so. Thanks for cooperation 🤖 Follow this PR Review Process:
If you have questions about anything, reach out in #jetpack-developers for guidance! |
Code Coverage SummaryCoverage changed in 4 files.
2 files are newly checked for coverage.
Full summary · PHP report · JS report If appropriate, add one of these labels to override the failing coverage check:
Covered by non-unit tests
|
|
We changed the approach and move to thrid_party |
Fixes #
Proposed changes
A payment made through a PayPal-hosted button, pay link or QR code never touches the site, so nothing on our side sees it. This PR makes PayPal tell the site, and the site tell WordPress.com.
PAYMENT.CAPTURE.COMPLETED,DECLINED,PENDING,REFUNDEDandREVERSEDevents, pointing at a newPOST wpcom/v2/paypal/webhookroute on the site. Registration happens when credentials are connected (manual or Partner Referrals), lazily from the connection-status endpoint for sites connected before this shipped, and again on an environment switch. A listener URL PayPal already knows is adopted rather than treated as a failure. The webhook is deleted on disconnect, before the credentials go.POST /v1/notifications/verify-webhook-signature) using the delivery'sPAYPAL-*headers, the raw body and the stored webhook id. Anything PayPal does not vouch for gets a 400.jetpack_paypal_capture_{completed,declined,pending,refunded,reversed}, attributed to the site's connection owner, with the capture id, status, amount, currency, order id, custom id, invoice id, merchant id, final-capture flag and environment. A notification PayPal delivers twice is recorded once.wpcom/v2/paypal/webhook-events, signed as the blog, so payments can be logged centrally the way the V1 buttons were. Only the fields above go over, never the payer. A transport error or a 5xx schedules a WP-Cron retry five minutes later, three attempts in all; a 4xx is taken as final.Why the site forwards instead of WordPress.com subscribing directly: PayPal delivers webhook events only to the REST app whose client id processed the payment. Our Partner Referrals flow is a
FIRST_PARTYintegration, so every payment link and button is created with the merchant's own credentials and a webhook on our platform app would never see a capture. The WordPress.com endpoint is a separate change; until it ships, the forward gets a 404 and stops after one attempt.Related product discussion/links
Does this pull request change what data or activity we track or use?
Yes. For sites using the API-managed PayPal buttons, each payment capture PayPal reports through the webhook is recorded as a
jetpack_paypal_capture_*Tracks event and forwarded to WordPress.com. The data is the capture's identifiers, amount, currency, status and the merchant's PayPal id. The buyer's name and email are never recorded or forwarded.Testing instructions
The site must be reachable by PayPal over HTTPS (a Jurassic Ninja site works) and the
paypal-payments-api-managed-buttonsfeature flag must be on.https://<site>/wp-json/wpcom/v2/paypal/webhookwith the fivePAYMENT.CAPTURE.*events should now exist. Thejetpack_paypal_payment_buttons_webhookoption holds its id, environment and URL.PAYMENT.CAPTURE.COMPLETEDdelivery should show a 200 from the site. In Tracks, ajetpack_paypal_capture_completedevent for the connection owner should appear with the capture id and amount."recorded": falseand no second Tracks event.curland a changed body or a missingPAYPAL-TRANSMISSION-SIGheader): the site answers 400.POST wpcom/v2/paypal/webhook-eventsper delivery answered 404 and no scheduledjetpack_paypal_webhook_forward_retrycron event. Simulating a 5xx (for example by filteringpre_http_requestfor that URL) schedules one retry five minutes later, and a third failed attempt schedules nothing.🤖 Generated with Claude Code