Resubscribe Flow Demo (Ticket 1527436)

Ticket 1527436. This page demonstrates a fix for the gap you described: a member whose plan was canceled because there was no card on file adds a card, and then has no idea that Resubscribe still has to be pressed.

Sign in first

Everything below happens inside the SureCart Customer Dashboard, so you have to be logged in as the demo member.

The demo member owns one plan, “Guild Membership”, at $29 per month. It is canceled and has no payment method on file, exactly like your migrated members.


Step 1. Put the demo back to the starting point

This cancels the plan again and removes every saved card, so you can run the walkthrough as many times as you like.

Step 2. Look at the plan screen

You will see the two buttons your members see: Add Payment Method and Resubscribe. With the fix active, both now carry a return address that brings the member straight back to this screen once the card is saved.

Step 3. Save a card and watch the hand off

This stands in for the card form. This demo store has no live card processor connected, only SureCart’s Test Processor, so the card entry screen itself cannot be shown here. The button below saves a card the same way a real processor does, then sends the member back exactly where Stripe would send them.

What you should see when you land: a green “Payment method saved. One last step: confirm below to restart your membership.” message, and SureCart’s own Resubscribe confirmation already open, showing the exact amount and the next renewal date. Press Yes, Reactivate and the plan goes straight back to Active.


What the fix actually changes

  • The Add Payment Method button normally drops the member on the dashboard home page, because it sends no return address at all. It now returns them to their plan.
  • The Resubscribe button normally sends the member to the “Update Payment Method” screen. After the card is saved they land back on a screen titled “Update Payment Method” with a big “Update Payment Method” button, which reads like the card still has not been saved. It now goes straight to the card form and comes straight back to the plan.
  • A clear confirmation is shown that the card was saved.
  • SureCart’s own Resubscribe confirmation opens by itself, so the only thing left to do is press Yes, Reactivate.

It only ever touches a plan that is canceled with no payment method on file. Members who already have a card keep the normal screens, so they can still switch between saved cards.

The snippet

Add this as a PHP snippet with WPCode, Code Snippets, or your child theme functions.php. Nothing else needs configuring.

<?php
/**
 * SureCart: make "add a card" and "resubscribe" one continuous flow.
 *
 * Only affects the customer dashboard Plan screen, and only for a plan that is
 * canceled with no payment method on file. Everything else is left alone.
 *
 *  1. Sends the "Add Payment Method" / "Resubscribe" buttons back to the Plan
 *     screen once the card is saved, instead of the dashboard home.
 *  2. Shows a clear confirmation that the card was saved.
 *  3. Opens SureCart's own Resubscribe confirmation, so the member only has to
 *     press "Yes, Reactivate".
 */
add_action(
	'wp_footer',
	function () {
		if ( is_admin() ) {
			return;
		}
		?>
<script id="sc-resubscribe-handoff">
( function () {
	var FLAG  = 'sc_resubscribe';
	var RETRY = 'sc_resubscribe_retry';

	function params() {
		return new URLSearchParams( window.location.search );
	}

	function planUrl( attempt ) {
		var u = new URL( window.location.href );
		[ 'success_url', 'payment_intent', 'payment_intent_client_secret', 'redirect_status', 'nonce', FLAG, RETRY ]
			.forEach( function ( k ) { u.searchParams.delete( k ); } );
		u.searchParams.set( 'model', 'subscription' );
		u.searchParams.set( 'action', 'edit' );
		u.searchParams.set( FLAG, '1' );
		if ( attempt ) { u.searchParams.set( RETRY, String( attempt ) ); }
		return u.toString();
	}

	function subscriptionEl() {
		return document.querySelector( 'sc-subscription' );
	}

	function model() {
		var el = subscriptionEl();
		return ( el && el.subscription ) ? el.subscription : null;
	}

	function hasPaymentMethod( m ) {
		return !! ( m && ( m.payment_method || m.manual_payment ) );
	}

	/* Only touch the one situation this is for: a canceled plan with nothing on
	   file to charge. Members who already have a card keep the stock screens. */
	function isStranded() {
		var m = model();
		return !! m && 'canceled' === m.status && ! hasPaymentMethod( m );
	}

	/* Point an "add a card" link back at the Plan screen. */
	function retarget( btn, subId ) {
		var href = btn.getAttribute( 'href' );
		if ( ! href || '1' === btn.dataset.scRetargeted || ! isStranded() ) { return; }

		var url;
		try { url = new URL( href, window.location.origin ); } catch ( e ) { return; }

		var action = url.searchParams.get( 'action' );
		if ( 'create' !== action && 'update_payment_method' !== action ) { return; }

		/* This is the only route that honours success_url. */
		url.searchParams.set( 'model', 'payment_method' );
		url.searchParams.set( 'action', 'create' );
		if ( subId ) { url.searchParams.set( 'id', subId ); }

		/* Keep the member on the same live/test side of the store. */
		if ( ! url.searchParams.has( 'live_mode' ) ) {
			var m = model();
			if ( m && false === m.live_mode ) { url.searchParams.set( 'live_mode', 'false' ); }
		}

		url.searchParams.set( 'success_url', planUrl() );

		btn.setAttribute( 'href', url.toString() );
		btn.dataset.scRetargeted = '1';
	}

	function banner( message ) {
		var host = subscriptionEl();
		if ( ! host || ! host.parentNode ) { return; }

		var box = document.getElementById( 'sc-pm-saved-notice' );
		if ( ! box ) {
			box = document.createElement( 'div' );
			box.id = 'sc-pm-saved-notice';
			box.setAttribute( 'role', 'status' );
			box.style.cssText = 'display:flex;gap:10px;align-items:flex-start;margin:0 0 20px;padding:14px 16px;'
				+ 'border:1px solid #b7e1c6;border-left:4px solid #2e9e5b;border-radius:6px;'
				+ 'background:#f2fbf5;color:#14532d;font-size:15px;line-height:1.45;';
			host.parentNode.insertBefore( box, host );
		}
		box.innerHTML = '<span aria-hidden="true" style="font-size:17px;line-height:1.2">&#10004;</span><span>' + message + '</span>';
	}

	var opened = false;

	/* Open SureCart's own Resubscribe confirmation. */
	function handleReturn() {
		var el = subscriptionEl();
		if ( ! el || ! el.shadowRoot ) { return false; }

		var dialog = el.shadowRoot.querySelector( 'sc-subscription-reactivate' );
		if ( ! dialog || ! dialog.subscription ) { return false; }

		var m = dialog.subscription;

		if ( ! hasPaymentMethod( m ) ) {
			/* The card can take a few seconds to reach the subscription. Reload a
			   couple of times, then fall back to plain instructions rather than a
			   promise we cannot keep. */
			var attempt = parseInt( params().get( RETRY ) || '0', 10 );
			if ( attempt < 3 ) {
				banner( '<strong>Payment method saved.</strong><br>Getting your plan ready&hellip;' );
				setTimeout( function () { window.location.replace( planUrl( attempt + 1 ) ); }, 3000 );
				return true;
			}
			banner( '<strong>Payment method saved.</strong><br>Press <strong>Resubscribe</strong> above to restart your membership.' );
			return true;
		}

		if ( 'canceled' !== m.status ) {
			banner( '<strong>You are all set.</strong><br>Your membership is active again.' );
			return true;
		}

		banner( '<strong>Payment method saved.</strong><br>One last step: confirm below to restart your membership.' );

		/* The dialog only loads its amount and renewal date once SureCart has
		   hydrated it, so wait for that before opening. */
		if ( ! dialog.classList.contains( 'hydrated' ) ) { return false; }

		if ( ! opened ) {
			opened = true;
			dialog.open = false;
			requestAnimationFrame( function () { dialog.open = true; } );
		}
		return true;
	}

	function tick() {
		var p     = params();
		var subId = p.get( 'id' ) || '';

		var el = subscriptionEl();
		if ( el && el.shadowRoot ) {
			el.shadowRoot.querySelectorAll( 'sc-button[href]' ).forEach( function ( b ) { retarget( b, subId ); } );
		}

		/* The card-picker screen has its own "Add New" button. */
		var picker = document.querySelector( 'sc-subscription-payment-method' );
		if ( picker && picker.shadowRoot ) {
			picker.shadowRoot.querySelectorAll( 'sc-button[href]' ).forEach( function ( b ) { retarget( b, subId ); } );
		}

		return '1' === p.get( FLAG ) ? handleReturn() : false;
	}

	var tries = 0;
	var timer = setInterval( function () {
		if ( tick() || ++tries > 80 ) { clearInterval( timer ); }
	}, 250 );
	tick();
} )();
</script>
		<?php
	},
	100
);
Scroll to Top