Skip to content

Prevent Stripe non-card payments from timing out prematurely#1652

Open
dd32 wants to merge 6 commits intoWordPress:productionfrom
dd32:fix/claude/1532-stripe-payment-timeout
Open

Prevent Stripe non-card payments from timing out prematurely#1652
dd32 wants to merge 6 commits intoWordPress:productionfrom
dd32:fix/claude/1532-stripe-payment-timeout

Conversation

@dd32
Copy link
Copy Markdown
Member

@dd32 dd32 commented Mar 12, 2026

Summary

  • Delayed payment methods (boleto, OXXO, etc.) complete the Stripe checkout session but payment settlement takes days. Previously the 24-hour CampTix timeout would expire these attendees before payment could complete, causing attendees to lose their tickets.
  • When returning from Stripe with an unpaid delayed payment session, attendees are now marked as pending instead of completed, so tickets are not issued until payment actually settles.
  • The timeout review now checks both draft and pending attendees. For pending Stripe payments, the timeout is delayed by resetting the attendee timestamp (up to a 7-day maximum safety limit).
  • Open Stripe sessions that have not expired according to Stripe are also given extra time before timing out.

Fixes #1532

Test plan

  • Purchase a ticket using a delayed payment method (e.g., boleto in test mode)
  • Verify the attendee is marked as pending (not published) after returning from Stripe
  • Wait for the daily timeout cron to run and verify the attendee is NOT timed out while payment is still pending
  • Complete the payment and verify the attendee is correctly marked as published on the next timeout review cycle
  • Verify that after 7 days of pending status, the attendee is allowed to time out
  • Verify standard card payments still work as before (immediate completion)

Generated with Claude Code

dd32 and others added 3 commits March 13, 2026 09:19
Delayed payment methods (boleto, OXXO, etc.) can take days to settle.
Previously, the 24-hour timeout would mark these attendees as timed out
before payment could complete.

Changes:
- Mark attendees as pending (not completed) when returning from Stripe
  with an unpaid delayed payment session
- Delay timeout for attendees with pending Stripe payments by resetting
  their timestamp, with a 7-day maximum safety limit
- Include pending attendees in the timeout review process
- Delay timeout for open Stripe sessions that have not yet expired

Fixes WordPress#1532

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 28, 2026 03:14
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates CampTix’s Stripe integration and timeout cron logic to better support Stripe delayed-settlement payment methods (boleto, OXXO, etc.) so attendees aren’t expired before payment actually settles.

Changes:

  • Treat Stripe Checkout sessions that are complete but still unpaid as CampTix pending instead of completed.
  • Extend the timeout sweep to include pending attendees and add Stripe-specific logic intended to delay timeout for pending/open sessions (with a 7‑day cap).
  • Improve Stripe “cancel” return handling by checking the Checkout session state before cancelling.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

File Description
public_html/wp-content/plugins/camptix/camptix.php Expands timeout review to include pending attendees and re-checks status after pre-timeout hooks.
public_html/wp-content/plugins/camptix/addons/payment-stripe.php Marks delayed Stripe payments as pending, adds session-aware timeout delay logic, and passes session ID through the cancel URL flow.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 457 to 465
$camptix->log( "Dying because couldn't get Payment status", $order['attendee_id'], compact( 'payment_token', 'payment_session' ) );
wp_die( 'could not find payment details' );
}

// Hmm.. Not finalised.
if ( 'open' === $session['status'] ) {
$payment_data = array(
'error' => 'Error during Payment checkout',
'data' => $session,
Comment on lines 7320 to 7325
do_action( 'camptix_pre_attendee_timeout', $attendee_id );

// Check the post_status again, incase a filter has caused the post to change.
if ( 'draft' !== get_post_field( 'post_status', $attendee_id ) ) {
// Check the post_status again, in case a hook has caused the post to change.
if ( ! in_array( get_post_field( 'post_status', $attendee_id ), array( 'draft', 'pending' ), true ) ) {
continue;
}
Comment on lines 7299 to 7303
'post_type' => 'tix_attendee',
'post_status' => 'draft',
'post_status' => array( 'draft', 'pending' ),
'posts_per_page' => 100,
'cache_results' => false,
'meta_query' => array(
Comment on lines +688 to 693
* 3. The session is still open and not expired -- delay the timeout.
*/
public function pre_attendee_timeout( $attendee_id ) {
/** @var CampTix_Plugin $camptix */
global $camptix;

Comment on lines +747 to +758
if ( 'open' === $session['status'] ) {
$expires_at = $session['expires_at'] ?? 0;

if ( $expires_at > time() ) {
$log_data = array( 'expires_at' => $expires_at );
$camptix->log( 'Stripe session still open, delaying timeout.', $attendee_id, $log_data );

$this->delay_attendee_timeout( $attendee_id );
return;
}
}
}
Copilot AI review requested due to automatic review settings April 28, 2026 03:46
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates CampTix’s Stripe integration to better support delayed-settlement payment methods (e.g., boleto/OXXO) by preventing premature attendee timeouts while the Stripe Checkout Session is complete but payment is still pending.

Changes:

  • Mark Stripe delayed-settlement returns as pending (instead of completed) when the Checkout Session is complete but payment_status is unpaid.
  • Expand the timeout sweep to include pending Stripe attendees and allow hooks to delay timeouts by updating the attendee timestamp (with a 7-day cap enforced in the Stripe addon).
  • Add PHPUnit coverage for the updated timeout sweep behavior in review_timeout_payments().

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
public_html/wp-content/plugins/camptix/camptix.php Extends timeout sweeping to include pending Stripe attendees and re-checks timestamp/status after the pre-timeout hook.
public_html/wp-content/plugins/camptix/addons/payment-stripe.php Implements pending handling on return/cancel, adds session retry logic, and delays timeouts for still-open/pending Stripe sessions with a max-age cap.
public_html/wp-content/plugins/camptix/tests/test-camptix-admin.php Adds tests ensuring the timeout sweep respects delayed timestamps and only sweeps pending Stripe attendees.
Comments suppressed due to low confidence (1)

public_html/wp-content/plugins/camptix/addons/payment-stripe.php:458

  • In payment_return(), the log context uses compact( 'payment_token', 'payment_session' ), but $payment_session is not defined (the variable in scope is $stripe_session). This will produce incorrect/empty log context and may trigger notices depending on error settings. Use the correct variable name so debugging data is accurate.
		if ( empty( $session['status'] ) ) {
			$camptix->log( "Dying because couldn't get Payment status", $order['attendee_id'], compact( 'payment_token', 'payment_session' ) );
			wp_die( 'could not find payment details' );

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 447 to +450
// Fetch the Payment details.
$stripe = new CampTix_Stripe_API_Client( $payment_token, $this->get_api_credentials()['api_secret_key'] );
$session = $stripe->get_session( $stripe_session );
$session = $this->get_session_with_retry( $stripe, $stripe_session );

Comment on lines +376 to +382
if ( ! $stripe_session || '{CHECKOUT_SESSION_ID}' === $stripe_session ) {
$stripe_session = get_post_meta( $order['attendee_id'], '_stripe_checkout_session_id', true );
}

if ( $stripe_session ) {
$stripe = new CampTix_Stripe_API_Client( $payment_token, $this->get_api_credentials()['api_secret_key'] );
$session = $this->get_session_with_retry( $stripe, $stripe_session );
Comment on lines +732 to +747
// Scenario 2: The checkout session completed but payment is still pending.
// This happens with delayed payment methods like boleto, OXXO, etc.
if ( 'complete' === $session['status'] && 'unpaid' === $payment_status ) {
$payment_intent = $session['payment_intent'] ?? array();
$payment_intent_status = is_array( $payment_intent ) ? ( $payment_intent['status'] ?? '' ) : '';

// Only delay timeout if the payment is still in a pending state.
if ( in_array( $payment_intent_status, array( 'requires_action', 'processing' ), true ) ) {
$log_data = array(
'payment_intent_status' => $payment_intent_status,
'payment_status' => $payment_status,
);
$camptix->log( 'Stripe payment still pending, delaying timeout.', $attendee_id, $log_data );

$this->delay_attendee_timeout( $attendee_id );
return;
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.

Stripe: non-card payments may timeout before payment completion.

2 participants