Signature
$result = apply_filters( 'mam_pmt_charge_card', array $data_array );
mam-stripe-manager implements this suite-wide payment filter (priority 10): it charges the customer’s saved card and returns the same array augmented with the outcome. Callers in the suite include MAM WooCommerce checkout/order capture, MAM WooCommerce Product Vendors (tips, held-payment capture), and the commission manager.
Trust boundary: apply this filter only from trusted server-side code with server-derived amounts. It moves real money.
Input keys
| Key | Type | Required | Meaning |
|---|---|---|---|
payment_method |
string |
yes | Must be 'stripe'. Any other value makes this plugin return the array untouched (so another processor can claim it). The key is removed before the result is returned. |
post_id |
int |
recommended | WooCommerce order id. Enables the amount ceiling, duplicate-intent reuse, idempotency keys, order currency, and order-meta persistence. |
amount |
float |
yes | Charge amount in decimal currency units (e.g. dollars). Zero-decimal currencies (JPY, KRW, …) are converted correctly. |
customer_id |
int |
yes | WordPress user id. The charge uses this user’s Stripe customer and their saved default payment method (_tsl_mam_stripe_payment_method user meta). Missing payment method → status = false. |
capture |
string |
no | 'no' creates an authorization hold (manual capture, PaymentIntent status requires_capture); anything else captures immediately. |
last_charge_id |
string |
no | An existing pi_… id: instead of creating a new intent, capture this one for amount. The intent’s wp_order_id metadata must match post_id (mismatch refuses the capture). |
is_tip |
bool |
no | Truthy marks the charge as a tip: the order-total amount ceiling and the duplicate-intent reuse are skipped (a second tip on the same order is a new, legitimate charge), and the idempotency key is tip-scoped. |
statement_descriptor |
string |
no | Overrides the charge description (default: vendor/site name + “Order {id}”; also used as the statement-descriptor suffix, truncated to 22 chars). |
Amount bounds
amount <= 0is a no-op:statusistrueonly when the order total is also ≤ 0 (a genuinely free order); otherwisefalse— a lost/tampered amount is not silently “successful”.- For non-tip charges with an order,
amountmay not exceedorder_total × tolerance + 0.01. The tolerance defaults to 1.2 because the checkout hold flow authorizes 120% of the order total, and is filterable:
apply_filters( 'mam_pmt_charge_amount_tolerance', 1.2 ); // return a float multiplier
Charges above the ceiling are refused (status = false, logged) without contacting Stripe.
Return shape
The input array, plus:
| Key | Type | Meaning |
|---|---|---|
status |
bool |
true when the PaymentIntent status is succeeded or requires_capture (a successful hold). |
charge |
StripePaymentIntent|null |
The intent on success; null on refusal/failure. |
error_message |
string |
Present only when a Stripe/API exception occurred — the exception message. (Legacy consumers may still read global $strip_error; new code should use error_message or the service’s last_error property.) |
Built-in safety (no caller action needed)
- Duplicate-submission guard — if the order already holds an intent for the same amount that succeeded (or an open hold when creating another hold), it is reused instead of charging again.
- Stripe idempotency key — derived from order/amount/currency/capture-mode/payment-method/attempt, so a network retry maps to the original intent.
- On success the order is stamped: transaction id,
_mam_stripe_intent_id,_stripe_charge_ids,_stripe_currency,_stripe_source_id,_stripe_charge_captured,_mam_payment_method, and paid-date meta (HPOS-safe CRUD writes).
Example — charge an order’s total
$order = wc_get_order( $order_id );
$result = apply_filters( 'mam_pmt_charge_card', array(
'payment_method' => 'stripe',
'post_id' => $order->get_id(),
'customer_id' => $order->get_customer_id(),
'amount' => (float) $order->get_total(),
'capture' => 'yes',
) );
if ( empty( $result['status'] ) ) {
$reason = $result['error_message'] ?? 'charge refused';
// mark the order failed, surface $reason
} else {
$intent_id = $result['charge']->id; // pi_...
}
Example — hold now, capture later
// 1. At checkout: hold 120% of the total
$hold = apply_filters( 'mam_pmt_charge_card', array(
'payment_method' => 'stripe',
'post_id' => $order_id,
'customer_id' => $user_id,
'amount' => $total * 1.2,
'capture' => 'no', // authorization hold
) );
// 2. At fulfillment: capture the real amount from the held intent
$capture = apply_filters( 'mam_pmt_charge_card', array(
'payment_method' => 'stripe',
'post_id' => $order_id,
'customer_id' => $user_id,
'amount' => $total,
'last_charge_id' => $hold['charge']->id,
) );
Related articles
- Hook:
mam_pmt_refund_card - Hook:
mam_stripe_connect_transfer - Plugin: mam-stripe-manager
Metadata
| Field | Value |
|---|---|
| Article type | Hook Reference |
| Plugin slug | mam-stripe-manager |
| Applies to plugin version | 26.19.1+ |
| Category | Extending MAM Suite |
| Hook type | filter (implemented by this plugin; applied by callers) |
| Audience | PHP developer |
| Last verified | 2026-06-10 |
