With a main site using WordPress/WooCommerce, and a remote site serving front end; is it possible to process checkout form using WooCommerce REST API, and receive a redirect URL to the selected Payment Gateway?

I tried creating a custom AJAX function, but It seems it requires a nonce field on the submitted form, and that is problematic for the front end is not on the same server.

1 Answer 1


To process payment for an WooCommerce Order, the workflow goes like the following:

  1. Create an WooCommerce Order
  2. Process Payment for this order, using one of WC_Payment_Gateways
  3. Change order status (default is pending->processing, or to complete if empty order)

IMHO, Step two can be implemented as the following:

  1. Use a front-end lib to tokenize payment info, using frameworks like Stripe or PayPal.
  2. Pass the payment token to your WordPress backend using WP REST API
  3. Let WordPress and WooCommerce process the payment as usual.

You can create a custom REST API endpoint to process payment for your WooCommerce website, add the following code to your code.

add_action( 'rest_api_init', 'wc_rest_payment_endpoints' );
function wc_rest_payment_endpoints() {
     * Handle Payment Method request.
    register_rest_route( 'wc/v2', 'payment', array(
        'methods'  => 'POST',
        'callback' => 'wc_rest_payment_endpoint_handler',
    ) );
function wc_rest_payment_endpoint_handler( $request = null ) {
    $response       = array();
    $parameters     = $request->get_json_params();
    $payment_method = sanitize_text_field( $parameters['payment_method'] );
    $order_id       = sanitize_text_field( $parameters['order_id'] );
    $payment_token  = sanitize_text_field( $parameters['payment_token'] );
    $error          = new WP_Error();

    if ( $payment_method === "stripe" ) {
        $wc_gateway_stripe                = new WC_Gateway_Stripe();
        $_POST['stripe_token']            = $payment_token;
        $payment_result               = $wc_gateway_stripe->process_payment( $order_id );
        if ( $payment_result['result'] === "success" ) {
            $response['code']    = 200;
            $response['message'] = __( "Your Payment was Successful", "wc-rest-payment" );
        } else {
            return new WP_REST_Response( array("c"), 123 );
            $response['code']    = 401;
            $response['message'] = __( "Please enter valid card details", "wc-rest-payment" );
    }  else {
        $response['code'] = 405;
        $response['message'] = __( "Please select an available payment method. Supported payment method can be found at https://wordpress.org/plugins/wc-rest-payment/#description", "wc-rest-payment" );

    return new WP_REST_Response( $response, 123 );

If you're looking for a hassle-free way to do this, you checkout our plugin WC REST Payment.

It supports WooCommerce Gateways including Stripe, PayPal Express, PayPal Standard, Direct Bank Transfer, Cheque, Cash on Delivery.

  • thank you for the thorough guide, but I opted for calling process_checkout using ajax some time ago. This site uses a lesser known payment gateway, so it's all or nothing when I call it on the front-end (no tokenization). But since I didn't mention it in the question I'm marking this as an answer. Commented Jan 26, 2018 at 7:17
  • how do you know what the decline codes are from stripe, I don't see how you would handle those here... Commented Mar 7, 2019 at 23:33
  • You can't, if you are using the WooCommerce Stripe Gateway. I've dig into their source code, they don't return the error. However, if you enable debugging log, you can see the error from WooCommerce -> Status -> Logs
    – Jack Song
    Commented Mar 8, 2019 at 0:00
  • @JackSong this works fine for Stripe, thank you plenty. What architecture would you suggest to implement for Paypal? I have found react libraries for the paypal buttons which seem to work just fine, would you on success make a separate call to wordpress to place the order with status: confirmed? It seems a little insecure to me which is why I am asking Commented May 6, 2020 at 12:56

Not the answer you're looking for? Browse other questions tagged or ask your own question.