27. PaymentIntent Flow | Form Forge - Build Forms with AI in Seconds
Download Log in

27. PaymentIntent Flow

Developer Guide

The complete payment flow validates the form before card capture. This prevents a customer from being charged for a submission that would later fail reCAPTCHA, required-field, or server validation. Payment is a PRO field: the admin palette renders it locked on Free, ajax_save() downgrades crafted Free-plan payment/map/calendar fields to text, and public render shows a PRO lock message if an old saved field exists after downgrade.

Stage 1 — Frontend: Initialize Stripe Elements

javascript
var stripe = Stripe( formforgeStripe.publishableKey );
var elements = stripe.elements();
var cardElement = elements.create( 'card', {
    style: {
        base: { fontSize: '16px', color: '#1e293b' },
        invalid: { color: '#ef4444' },
    }
} );
cardElement.mount( '#formforge-stripe-card-element' );

Stage 2 — AJAX: Validate Submission First

javascript
var validationData = new FormData( formElement );
validationData.append( 'action', 'formforge_validate_submission' );

fetch( formforgeFront.ajaxUrl, {
    method: 'POST',
    body: validationData
} ).then( function( response ) {
    return response.json();
} ).then( function( response ) {
    if ( ! response.success ) {
        showError( response.data.message );
        return;
    }
    // Continue to PaymentIntent creation only after validation passes.
} );

Validation errors carry routing metadata when possible:

Response keyMeaning
field_idField-level validation error; Conversational Mode can jump to that field’s step.
payment_field_idPayment-specific error; Conversational Mode can jump to the Stripe field and show the error near the Card Element.
neither keyGlobal submit/server/reCAPTCHA error; keep the visitor on the current step and show a global error.

Stage 3 — AJAX: Create PaymentIntent

javascript
jQuery.post( formforgeFront.ajaxUrl, {
    action: 'formforge_create_payment_intent',
    nonce: formforgeFront.nonce,
    amount: 2500,
    currency: 'usd',
    form_id: 1
}, function( response ) {
    if ( response.success ) {
        var clientSecret = response.data.client_secret;
    }
} );

Current frontend code sends a FormData payload built from the form instead of only the scalar amount/currency fields. This lets the server see the posted Calculation source for dynamic payments and reject a missing, stale, negative, below-minimum, or mismatched dynamic amount before Stripe sees a PaymentIntent request.

Stage 4 — Frontend: Confirm Payment

javascript
stripe.confirmCardPayment( clientSecret, {
    payment_method: { card: cardElement }
} ).then( function( result ) {
    if ( result.error ) {
        showError( result.error.message );
    } else if ( result.paymentIntent.status === 'succeeded' ) {
        // Write only to hidden inputs inside the payment field wrapper.
        // Do not use a broad document.getElementById(fieldName) target:
        // duplicate/colliding IDs can point at a visible Email input.
        paymentField.querySelector( 'input[type="hidden"]#field_payment_payment_intent' )
            .value = result.paymentIntent.id;
        document.querySelector( '.formforge-form' ).submit();
    }
} );

The browser runtime scopes the PaymentIntent write to hidden inputs inside .formforge-payment-field. This keeps optional Email fields empty when the visitor did not provide an email, even if legacy/AI-created markup has a colliding DOM id. Conversational mode sets a visible loading state on .ff-conv-submit-btn before it clicks the hidden submit button, then clears that state on validation or server errors.

Stage 5 — Server: Stripe API Call

text
POST https://api.stripe.com/v1/payment_intents
Authorization: Bearer sk_test_...
Content-Type: application/x-www-form-urlencoded

amount=2500&currency=usd&automatic_payment_methods[enabled]=true&metadata[form_id]=1

Forge AI Assistant Online

Hi! I'm the Form Forge AI assistant. Ask me anything about the plugin — setup, features, troubleshooting, or development.

Just now
Powered by Forge AI · Browse docs