* @return string Payment title.
private function get_payment_title() {
$customer_name = $this->get_customer_name();
$customer_email = $this->get_customer_email();
* @return string Customer name.
private function get_customer_name() {
$customer_name = $this->api->get_customer( 'name' );
$charge_details = $this->api->get_charge_details( [ 'name' ] );
if ( ! empty( $charge_details['name'] ) ) {
return $charge_details['name'];
* @return string Customer email.
private function get_customer_email() {
$customer_email = $this->api->get_customer( 'email' );
$charge_details = $this->api->get_charge_details( [ 'email' ] );
if ( ! empty( $charge_details['email'] ) ) {
return $charge_details['email'];
// phpcs:disable WordPress.Security.NonceVerification.Missing
if ( isset( $_POST['wpforms']['payment_link_email'] ) ) {
return sanitize_email( wp_unslash( $_POST['wpforms']['payment_link_email'] ) );
// phpcs:enable WordPress.Security.NonceVerification.Missing
* Logic that helps decide if we should send completed payments notifications.
* @param bool $process Whether to process or not.
* @param array $fields Form fields.
* @param array $form_data Form data.
* @param int $notification_id Notification ID.
public function process_email( $process, $fields, $form_data, $notification_id ) {
_deprecated_function( __METHOD__, '1.9.5 of the WPForms plugin', 'WPFormsStripe\Process::process_email()' );
if ( ! Helpers::has_stripe_enabled( [ $form_data ] ) ) {
if ( empty( $form_data['settings']['notifications'][ $notification_id ]['stripe'] ) ) {
if ( empty( $this->api->get_payment() ) ) {
if ( ! $this->is_payment_processed ) {
return empty( $this->api->get_error() );
* Update entry details for a successful payment.
* @param array $fields Final/sanitized submitted field data.
* @param array $entry Copy of original $_POST.
* @param array $form_data Form data and settings.
* @param string $entry_id Entry ID.
public function process_entry_data( $fields, $entry, $form_data, $entry_id ) {
$payment = $this->api->get_payment();
if ( empty( $payment->id ) || empty( $entry_id ) ) {
wpforms()->obj( 'entry' )->update(
* Get general errors before payment processing.
protected function get_entry_errors() {
// Check for Stripe payment tokens (card token or payment id).
$error = $this->api->get_error();
// Check for Stripe keys.
if ( ! Helpers::has_stripe_keys() ) {
return esc_html__( 'Stripe payment stopped, missing keys.', 'wpforms-lite' );
// Check that, despite how the form is configured, the form and
// entry actually contain payment fields, otherwise no need to proceed.
if ( ! wpforms_has_payment( 'form', $this->form_data ) || ! wpforms_has_payment( 'entry', $this->fields ) ) {
return esc_html__( 'Stripe payment stopped, missing payment fields.', 'wpforms-lite' );
// Check total charge amount.
if ( empty( $this->amount ) || wpforms_sanitize_amount( 0 ) === $this->amount ) {
return esc_html__( 'Stripe payment stopped, invalid/empty amount.', 'wpforms-lite' );
if ( 50 > ( $this->amount * 100 ) ) {
return esc_html__( 'Stripe payment stopped, amount less than minimum charge required.', 'wpforms-lite' );
public function process_payment() {
if ( $this->is_api_errors() ) {
// Proceed to executing the purchase.
if ( ! empty( $this->settings['enable_recurring'] ) || ! empty( $this->settings['recurring']['enable'] ) ) {
$this->process_payment_subscription();
$this->process_payment_single();
* Process a subscription payment for forms with old payments interface.
protected function process_legacy_payment_subscription() {
if ( ! $this->is_recurring_settings_ok( $this->settings['recurring'] ) ) {
$args = $this->get_base_subscription_args();
$args['settings'] = $this->settings['recurring'];
$args['email'] = sanitize_email( $this->fields[ $args['settings']['email'] ]['value'] );
$args['customer_name'] = ! empty( $args['settings']['customer_name'] ) ? sanitize_text_field( $this->fields[ $args['settings']['customer_name'] ]['value'] ) : '';
$args['customer_phone'] = ! empty( $args['settings']['customer_phone'] ) ? sanitize_text_field( $this->fields[ $args['settings']['customer_phone'] ]['value'] ) : '';
if ( wpforms()->is_pro() && isset( $args['settings']['customer_address'] ) && $args['settings']['customer_address'] !== '' ) {
$args['customer_address'] = $this->map_address_field( $this->fields[ $args['settings']['customer_address'] ], $args['settings']['customer_address'] );
// Set plan id to get correct mapped meta.
// Customer custom metadata.
$args['customer_metadata'] = $this->get_mapped_custom_metadata( 'customer' );
$this->process_subscription( $args );
// Set payment processing flag.
$this->is_payment_processed = true;
* Process a single payment.
public function process_payment_single() {
$amount_decimals = wpforms_get_currency_multiplier();
// Define the basic payment details.
'amount' => $this->amount * $amount_decimals,
'currency' => strtolower( wpforms_get_currency() ),
'form_name' => sanitize_text_field( $this->form_data['settings']['form_title'] ),
'form_id' => $this->form_id,
if ( ! Helpers::is_license_ok() && Helpers::is_application_fee_supported() ) {
$args['application_fee_amount'] = (int) ( round( $this->amount * 0.03, 2 ) * $amount_decimals );
// Store spam reason if exists.
if ( isset( $this->form_data['spam_reason'] ) ) {
$args['metadata']['spam_reason'] = $this->form_data['spam_reason'];
if ( ! empty( $this->settings['payment_description'] ) ) {
$args['description'] = html_entity_decode( $this->settings['payment_description'], ENT_COMPAT, 'UTF-8' );
if ( isset( $this->settings['receipt_email'] ) && $this->settings['receipt_email'] !== '' && ! empty( $this->fields[ $this->settings['receipt_email'] ]['value'] ) ) {
$args['receipt_email'] = sanitize_email( $this->fields[ $this->settings['receipt_email'] ]['value'] );
if ( isset( $this->settings['customer_email'] ) && $this->settings['customer_email'] !== '' && ! empty( $this->fields[ $this->settings['customer_email'] ]['value'] ) ) {
$args['customer_email'] = sanitize_email( $this->fields[ $this->settings['customer_email'] ]['value'] );
if ( isset( $this->settings['customer_name'] ) && $this->settings['customer_name'] !== '' && ! empty( $this->fields[ $this->settings['customer_name'] ]['value'] ) ) {
$args['customer_name'] = sanitize_text_field( $this->fields[ $this->settings['customer_name'] ]['value'] );
if ( isset( $this->settings['customer_phone'] ) && $this->settings['customer_phone'] !== '' && ! empty( $this->fields[ $this->settings['customer_phone'] ]['value'] ) ) {
$args['customer_phone'] = sanitize_text_field( $this->fields[ $this->settings['customer_phone'] ]['value'] );
// Customer custom metadata.
$args['customer_metadata'] = $this->get_mapped_custom_metadata( 'customer' );
$args = $this->payment_single_map_address( $args );
$this->api->process_single( $args );
// Set payment processing flag.
$this->is_payment_processed = true;
$this->update_credit_card_field_value();
$this->process_api_error( 'single' );
* Map address field for single payment.
* @param array $args Payment arguments.
private function payment_single_map_address( array $args ): array {
if ( ! wpforms()->is_pro() ) {
if ( isset( $this->settings['customer_address'] ) && $this->settings['customer_address'] !== '' ) {
$args['customer_address'] = $this->map_address_field( $this->fields[ $this->settings['customer_address'] ], $this->settings['customer_address'] );
if ( isset( $this->settings['shipping_address'] ) && $this->settings['shipping_address'] !== '' ) {
$args['shipping']['name'] = $args['customer_name'] ?? '';
$args['shipping']['address'] = $this->map_address_field( $this->fields[ $this->settings['shipping_address'] ], $this->settings['shipping_address'] );
* Process a subscription payment.
public function process_payment_subscription() {
if ( Helpers::is_legacy_payment_settings( $this->form_data ) ) {
$this->process_legacy_payment_subscription();
$args = $this->get_base_subscription_args();
foreach ( $this->settings['recurring'] as $key => $recurring ) {
if ( ! $this->is_subscription_plan_valid( $recurring ) ) {
$args['email'] = sanitize_email( $this->fields[ $recurring['email'] ]['value'] );
$args['settings'] = $recurring;
$args['description'] = sanitize_text_field( $recurring['name'] );
if ( isset( $recurring['customer_name'] ) && $recurring['customer_name'] !== '' && ! empty( $this->fields[ $recurring['customer_name'] ]['value'] ) ) {
$args['customer_name'] = sanitize_text_field( $this->fields[ $recurring['customer_name'] ]['value'] );
if ( isset( $recurring['customer_phone'] ) && $recurring['customer_phone'] !== '' && ! empty( $this->fields[ $recurring['customer_phone'] ]['value'] ) ) {
$args['customer_phone'] = sanitize_text_field( $this->fields[ $recurring['customer_phone'] ]['value'] );
if ( wpforms()->is_pro() && isset( $recurring['customer_address'] ) && $recurring['customer_address'] !== '' ) {
$args['customer_address'] = $this->map_address_field( $this->fields[ $recurring['customer_address'] ], $recurring['customer_address'] );
// Customer custom metadata.
$args['customer_metadata'] = $this->get_mapped_custom_metadata( 'customer' );
// Validate the number of cycle to process.
if ( ! empty( $recurring['cycles'] ) && ( $recurring['cycles'] === 'undefined' || ( is_numeric( $recurring['cycles'] ) && $recurring['cycles'] > 0 ) ) ) {
$args['cycles'] = sanitize_text_field( $recurring['cycles'] );
$this->process_subscription( $args );
if ( ! empty( $this->settings['enable_one_time'] ) ) {
$this->process_payment_single();
esc_html__( 'Stripe Subscription payment stopped, validation error.', 'wpforms-lite' ),
* Validate plan before process.
* @param array $plan Plan settings.
protected function is_subscription_plan_valid( $plan ) {
return ! empty( $plan['email'] ) && $this->is_recurring_settings_ok( $plan );
* Update the credit card field value to contain basic details.
public function update_credit_card_field_value() {
foreach ( $this->fields as $field_id => $field ) {
if ( empty( $field['type'] ) || $this->api->get_config( 'field_slug' ) !== $field['type'] ) {
$details = $this->api->get_charge_details( [ 'name', 'last4', 'brand' ] );
if ( ! empty( $details['last4'] ) ) {
$details['last4'] = 'xxxx xxxx xxxx ' . $details['last4'];
if ( ! empty( $details['brand'] ) ) {
$details['brand'] = ucfirst( $details['brand'] );
$details = is_array( $details ) && ! empty( $details ) ? implode( "\n", array_filter( $details ) ) : '-';
* This filter allows to overwrite a Style Credit Card value in saved entry.
* @param array $details Card details.
* @param object $payment Stripe payment objects.
wpforms()->obj( 'process' )->fields[ $field_id ]['value'] = apply_filters( 'wpforms_stripe_creditcard_value', $details, $this->api->get_payment() ); // phpcs:ignore WPForms.PHP.ValidateHooks.InvalidHookName
* Check if there is at least one visible (not hidden by conditional logic) card field in the form.
protected function is_card_field_visibility_ok() {
// If the form contains no fields with conditional logic the card field is visible by default.
if ( empty( $this->form_data['conditional_fields'] ) ) {
foreach ( $this->fields as $field ) {
if ( empty( $field['type'] ) || $this->api->get_config( 'field_slug' ) !== $field['type'] ) {
// if the field is NOT in array of conditional fields, it's visible.
if ( ! in_array( $field['id'], $this->form_data['conditional_fields'], true ) ) {
// if the field IS in array of conditional fields and marked as visible, it's visible.
if ( ! empty( $field['visible'] ) ) {
* @param string $title Error title.
* @param string $message Error message.
* @param string $level Error level to add to 'payment' error level.
protected function log_error( $title, $message = '', $level = 'error' ) {