Skip to main content

Purchase a buy-up from custom code, safely

Premium Intermediate

Two things make the buy-up purchase endpoint different from a plain authenticated POST, and both are easy to get wrong from outside Benecaster’s own UI.

You have to state the amount. confirm_amount_cents is required — the price, in integer minor units, that the subscriber agreed to pay. The server compares it against the live price and returns 409 amount_mismatch when they disagree, with the current amount_cents and currency in the error data so you can re-render and ask again. That is what stops a price change between render and click from being charged silently.

Send cents, not dollars. "5.00" is a whole number, so it passes the format check and is read as five cents — it comes back as amount_mismatch, not as a validation error. If you are seeing a mismatch on a price nobody changed, this is almost always why.

Send an idempotency key if you intend to retry. Idempotency-Key is optional, but it is the difference between a retry returning the original 201 with the same grant_id and returning a confusing 409 already_subscribed. Mint one per user action and reuse it only for retries of that action — then retire it once the server has answered with an error, because at that point nothing was created and the next attempt is a genuinely new purchase.

Buy-ups belong to one show. A {buyup_id} from a different show returns 404, not 403 — the endpoint will not confirm that an ID exists elsewhere. Resolve buy-ups for the show you are working on rather than assuming an install-wide list.

Full endpoint reference: Buy-ups Subscriber REST API.

Code

<?php
/**
 * Purchase a buy-up on behalf of the logged-in subscriber.
 *
 * @param int    $show_id      Show the subscriber holds a native subscription on.
 * @param int    $buyup_id     Buy-up belonging to that show.
 * @param int    $amount_cents The price shown to the subscriber, in minor units.
 * @param string $action_key   Stable per user action; reuse only when retrying it.
 */
function my_addon_purchase_buyup( int $show_id, int $buyup_id, int $amount_cents, string $action_key ) {
    $request = new WP_REST_Request(
        'POST',
        "/benecaster/v1/shows/{$show_id}/buyups/{$buyup_id}/subscribe"
    );
    $request->set_header( 'Idempotency-Key', $action_key );
    $request->set_body_params( [ 'confirm_amount_cents' => $amount_cents ] );

    $response = rest_do_request( $request );

    if ( $response->is_error() ) {
        $error = $response->as_error();

        // The price moved between render and click. Re-render at the new
        // amount and ask again — do not silently resubmit with the new
        // figure, because the subscriber never agreed to it.
        if ( 'amount_mismatch' === $error->get_error_code() ) {
            $data = $error->get_error_data();
            return new WP_Error(
                'price_changed',
                sprintf(
                    /* translators: %s: formatted current price */
                    __( 'The price is now %s. Please confirm the new amount.', 'my-addon' ),
                    number_format_i18n( $data['amount_cents'] / 100, 2 )
                ),
                $data
            );
        }

        return $error;
    }

    return $response->get_data(); // grant_id, stripe_subscription_item_id, buyup_id
}

View on GitHub →

Need this built rather than just documented? See our services →