Miguel B. Posted February 4, 2026 at 10:48 AM Share Posted February 4, 2026 at 10:48 AM (edited) I wanted to share a simple setup where I’m using JReviews listings as purchasable products with Stripe Checkout, without turning the site into a full e-commerce system. This is not a full marketplace implementation, but a lightweight, centralized setup designed for a specific use case. In short, this code does the following: Listings are created by users (each listing belongs to a seller) Sellers can optionally enable a “Shop now” button using a custom radio field The button only appears if: "a price is set" and "a supported shipping option is selected" Shipping is chosen from a predefined list (package size/weight), and the actual cost is calculated in PHP Clicking the button redirects the buyer to a custom Stripe Checkout session The platform receives the payment first and handles delivery confirmation and payouts later This results in a lightweight, centralized marketplace flow: no carts, no WooCommerce, no forced checkout, just listings with an optional purchase path. Below is a simplified example of the template logic used to display the button. Step 1: Define price and shipping using custom fields The first step was to define price and shipping logic using JReviews custom fields. I created: A decimal custom field for the product price A single-select custom field for shipping, where the listing owner selects a predefined package type (size/weight) The shipping example shown here works for my specific use case, where shipping is limited to a single country. Shipping costs are predefined and managed centrally, which keeps the setup simple. Different setups may require different shipping logic depending on their needs. Step 2: Conditional “Shop now” button in details.thtml In this step, I add a conditional “Shop now" button to the listing detail page. The "Shop now" button: Is optional and controlled by the listing owner using a radio custom field (“activate / don’t activate”). Only appears if a valid price and a supported shipping option are set Calculates the shipping cost internally based on a predefined option Redirects the buyer to a custom Stripe Checkout endpoint This keeps checkout logic centralized and avoids exposing pricing rules to users. <?php $payment_option = $CustomFields->fieldValue('jr_opcionescobrar', $listing); $precio_raw = $CustomFields->fieldValue('jr_precio', $listing); $opcion_envio = $CustomFields->fieldValue('jr_gastosdeenvioespana', $listing); $precio = (float) str_replace(',', '.', $precio_raw); // Predefined shipping prices (mapped internally) $precios_envio = [ 'envio-gratis' => 0, 'envio-ligero' => 2.95, 'paquete-pequeno' => 4.95, 'paquete-mediano' => 6.95, 'paquete-grande' => 8.95, 'voluminoso-especial' => null ]; // Resolve shipping cost $envio = null; if (isset($precios_envio[$opcion_envio])) { $envio = $precios_envio[$opcion_envio]; } /* ----------- start internal CTA ------------ */ // Show button only if secure payment is enabled // and the listing has valid price + supported shipping if ( $payment_option === 'pago-seguro' && $precio > 0 && $envio !== null ) : $url_pago = home_url('/pago-seguro?') . http_build_query([ 'anuncio_id' => (int) $listing['Listing']['listing_id'], 'precio' => $precio, 'envio' => $envio, 'titulo' => sanitize_text_field($listing['Listing']['title']), 'artesano_id' => (int) $listing['Listing']['user_id'] ]); ?> <p> <a class="wp-block-button__link wp-element-button" href="<?php echo esc_url($url_pago); ?>" style="display:block;width:100%;text-align:center;" aria-label="Shop now"> <strong>Shop now</strong> </a> </p> <?php endif; ?> Step 3: Create a Stripe Checkout session This endpoint is called when the buyer clicks the “Shop now” button. Its responsibility is intentionally small and focused: Receive the listing data from the button (ID, price, shipping, seller) Build Stripe line items for product and shipping Create a Stripe Checkout Session Redirect the buyer to Stripe to complete the payment The buyer only pays product + shipping. No marketplace fees are shown during checkout. <?php /** * Technical endpoint to create a Stripe Checkout session * This is not a visual page and does not load headers or footers */ // Load Stripe require_once get_template_directory() . '/stripe/init.php'; // Stripe API key (stored in wp-config.php) \Stripe\Stripe::setApiKey(STRIPE_SECRET_KEY); // =============================== // Read parameters from the button // =============================== $anuncio_id = isset($_GET['anuncio_id']) ? (int) $_GET['anuncio_id'] : 0; $precio = isset($_GET['precio']) ? (float) str_replace(',', '.', $_GET['precio']) : 0; $envio = isset($_GET['envio']) ? (float) str_replace(',', '.', $_GET['envio']) : 0; $titulo = isset($_GET['titulo']) ? sanitize_text_field($_GET['titulo']) : 'Product'; $artesano_id = isset($_GET['artesano_id']) ? (int) $_GET['artesano_id'] : 0; // =============================== // Basic validation // =============================== if ($anuncio_id <= 0 || $precio <= 0) { wp_die('Invalid payment parameters.'); } // =============================== // Build Stripe line items // =============================== $line_items = []; // Product $line_items[] = [ 'price_data' => [ 'currency' => 'eur', 'product_data' => [ 'name' => $titulo, ], 'unit_amount' => (int) round($precio * 100), ], 'quantity' => 1, ]; // Shipping if ($envio > 0) { $line_items[] = [ 'price_data' => [ 'currency' => 'eur', 'product_data' => [ 'name' => 'Shipping', ], 'unit_amount' => (int) round($envio * 100), ], 'quantity' => 1, ]; } // =============================== // Create Stripe Checkout session // =============================== try { $session = \Stripe\Checkout\Session::create([ 'locale' => 'es', 'mode' => 'payment', 'payment_method_types' => ['card'], 'line_items' => $line_items, 'success_url' => home_url('/gracias-por-tu-compra/?session_id={CHECKOUT_SESSION_ID}'), 'cancel_url' => home_url('/pago-cancelado/'), 'metadata' => [ 'anuncio_id' => $anuncio_id, 'artesano_id' => $artesano_id, 'buyer_id' => get_current_user_id(), ], 'customer_creation' => 'always', 'shipping_address_collection' => [ 'allowed_countries' => ['ES'], ], 'phone_number_collection' => [ 'enabled' => true, ], ]); // Redirect to Stripe Checkout wp_redirect($session->url); exit; } catch (\Exception $e) { wp_die('Error creating payment: ' . esc_html($e->getMessage())); } Final notes: The checkout flow also relies on standard WordPress pages for handling success and cancellation states (for example “Payment successful” and “Payment cancelled”), which are used as return URLs from Stripe Checkout. The endpoint file lives in themes/your-theme/ The Stripe PHP library is included inside the theme (for example in themes/your-theme/stripe/) The Stripe secret key is defined in wp-config.php define('STRIPE_SECRET_KEY', 'your key here'); Edited February 4, 2026 at 10:55 AM by Miguel B. Josh Journey and Alejandro 2 Link to comment
Alejandro Posted February 4, 2026 at 01:42 PM Share Posted February 4, 2026 at 01:42 PM This section is only visible with a valid subscription. If you have a valid subscription, please login. Miguel B. 1 Link to comment
Miguel B. Posted February 4, 2026 at 05:58 PM Author Share Posted February 4, 2026 at 05:58 PM (edited) This section is only visible with a valid subscription. If you have a valid subscription, please login. Edited February 4, 2026 at 05:58 PM by Miguel B. Josh Journey 1 Link to comment
Recommended Posts