namespace WPForms\Integrations\Square;
use WPForms\Integrations\Square\Api\Api;
use WPForms\Vendor\Square\Models\Card;
use WPForms\Vendor\Square\Models\ErrorCode;
* Square payment processing.
* Sanitized submitted field values and data.
* Form submission raw data ($_POST).
* Form data and settings.
* Square payment settings.
* Square credit card field.
* Main class that communicates with the Square API.
* Save matched subscription settings.
private $subscription_settings = [];
* Whether the payment has been processed.
protected $is_payment_processed = false;
public function init(): Process {
protected function hooks() {
add_action( 'wpforms_process', [ $this, 'process_entry' ], 10, 3 );
add_action( 'wpforms_process_entry_saved', [ $this, 'update_entry_meta' ], 10, 4 );
add_filter( 'wpforms_forms_submission_prepare_payment_data', [ $this, 'prepare_payment_data' ], 10, 3 );
add_filter( 'wpforms_forms_submission_prepare_payment_meta', [ $this, 'prepare_payment_meta' ], 10, 3 );
add_action( 'wpforms_process_payment_saved', [ $this, 'process_payment_saved' ], 10, 3 );
* Check if a payment exists with an entry, if so validate and process.
* @param array $fields Final/sanitized submitted fields data.
* @param array $entry Copy of original $_POST.
* @param array $form_data Form data and settings.
public function process_entry( $fields, array $entry, array $form_data ) {
$fields = (array) $fields;
// Check if payment method exists and is enabled.
if ( ! Helpers::is_payments_enabled( $form_data ) ) {
$this->form_data = $form_data;
$this->form_id = isset( $form_data['id'] ) ? (int) $form_data['id'] : 0;
$this->settings = $form_data['payments']['square'];
$this->cc_field = $this->get_credit_card_field();
$this->currency = $this->get_currency();
$this->amount = $this->get_amount();
$this->connection = Connection::get();
// Before proceeding, check if any basic errors were detected.
if ( ! $this->is_form_processed() ) {
$this->api = new Api( $this->connection );
// Set tokens provided by Web Payments SDK.
$this->api->set_payment_tokens( $entry );
// Proceed to executing the purchase.
$this->process_payment();
// Update the card field value to contain basic details.
$this->update_credit_card_field_value();
* Bypass captcha if payment has been processed.
* @param bool $bypass_captcha Whether to bypass captcha.
public function bypass_captcha( $bypass_captcha ): bool {
_deprecated_function( __METHOD__, '1.9.6 of the WPForms plugin' );
if ( (bool) $bypass_captcha ) {
return $this->is_payment_processed;
* Check if form has errors before payment processing.
private function is_form_processed(): bool {
// Bail in case there are form processing errors.
if ( ! empty( wpforms()->obj( 'process' )->errors[ $this->form_id ] ) ) {
if ( ! $this->is_card_field_visibility_ok() ) {
return $this->is_form_ok();
* Check form settings, fields, etc.
private function is_form_ok(): bool {
// Check for Square connection.
if ( ! $this->is_connection_ok() ) {
$error_title = esc_html__( 'Square payment stopped, account connection is missing.', 'wpforms-lite' );
$this->errors[] = $error_title;
$this->log_errors( $error_title );
// Check total charge amount.
// Square has different minimum amount limits by country.
if ( ! $this->is_amount_ok() ) {
$error_title = esc_html__( 'Square payment stopped, amount is smaller than the allowed minimum amount for a payment.', 'wpforms-lite' );
$this->errors[] = $error_title;
'amount' => $this->amount,
'currency' => $this->currency,
// Check that, despite how the form is configured, the form and
// entry actually contain payment fields, otherwise no need to proceed.
if ( empty( $this->cc_field ) || ! wpforms_has_payment( 'form', $this->fields ) ) {
$error_title = esc_html__( 'Square payment stopped, missing payment fields.', 'wpforms-lite' );
$this->errors[] = $error_title;
$this->log_errors( $error_title );
* Check if the Square credit card field in the form is visible (not hidden by conditional logic).
private function is_card_field_visibility_ok(): bool {
// If the form doesn't contain the credit card field.
if ( empty( $this->cc_field ) ) {
// If the form contains no fields with conditional logic, the credit card field is visible by default.
if ( empty( $this->form_data['conditional_fields'] ) ) {
// If the credit card field is NOT in array of conditional fields, it's visible.
if ( ! in_array( $this->cc_field['id'], $this->form_data['conditional_fields'], true ) ) {
// If the credit card field IS in array of conditional fields and marked as visible, it's visible.
if ( ! empty( $this->cc_field['visible'] ) ) {
* Check if connection exists, configured and valid.
private function is_connection_ok(): bool {
return $this->connection !== null && $this->connection->is_usable();
* Check if an amount is greater than the minimum amount.
* @link https://developer.squareup.com/docs/build-basics/working-with-monetary-amounts#monetary-amount-limits
private function is_amount_ok(): bool {
$amount = Helpers::format_amount( $this->amount );
if ( $amount < 1 && in_array( $this->currency, [ 'USD', 'CAD' ], true ) ) {
if ( $amount < 100 && in_array( $this->currency, [ 'EUR', 'GBP', 'AUD', 'JPY' ], true ) ) {
private function process_payment() {
if ( ! empty( $this->settings['enable_recurring'] ) ) {
$this->process_payment_subscription();
$this->process_payment_single();
* Process a single payment.
protected function process_payment_single() {
$args = $this->get_payment_args_single();
$this->api->process_single_transaction( $args );
// Set payment processing flag.
$this->is_payment_processed = true;
$this->process_api_errors( 'single' );
* Process a subscription payment.
private function process_payment_subscription() {
$args = $this->get_payment_args_general();
foreach ( $this->settings['recurring'] as $recurring ) {
if ( ! $this->is_subscription_plan_valid( $recurring ) ) {
// Put subscription arguments into its own key.
$args['subscription'] = $this->get_payment_args_subscription( $recurring );
$this->subscription_settings = $args['subscription'];
$this->api->process_subscription_transaction( $args );
// Set payment processing flag.
$this->is_payment_processed = true;
$this->process_api_errors( 'subscription' );
if ( ! empty( $this->settings['enable_one_time'] ) ) {
$this->process_payment_single();
esc_html__( 'Square Subscription payment stopped, validation error.', 'wpforms-lite' ),
* Retrieve subscription payment args.
* @param array $plan Plan settings.
private function get_payment_args_subscription( array $plan ): array {
$args_sub['customer']['email'] = sanitize_email( $this->fields[ $plan['customer_email'] ]['value'] );
$args_sub['customer']['first_name'] = sanitize_text_field( $this->fields[ $plan['customer_name'] ]['first'] );
$args_sub['customer']['last_name'] = sanitize_text_field( $this->fields[ $plan['customer_name'] ]['last'] );
// If a Name field has the `Simple` format.
empty( $args_sub['customer']['first_name'] ) &&
empty( $args_sub['customer']['last_name'] ) &&
! empty( $this->fields[ $plan['customer_name'] ]['value'] )
$args_sub['customer']['last_name'] = sanitize_text_field( $this->fields[ $plan['customer_name'] ]['value'] );
if ( isset( $plan['customer_address'] ) && $plan['customer_address'] !== '' && wpforms()->is_pro() ) {
$args_sub['customer']['address'] = $this->fields[ $plan['customer_address'] ];
$cadences_list = Helpers::get_subscription_cadences();
$phase_cadence = $cadences_list[ $plan['phase_cadence'] ] ?? $cadences_list['yearly'];
$args_sub['phase_cadence'] = $phase_cadence;
$plan_name = $this->get_form_name();
$plan_name .= empty( $plan['name'] ) ? '' : ': ' . $plan['name'];
$args_sub['plan_name'] = sprintf( '%s (%s)', $plan_name, $phase_cadence['name'] );
$args_sub['plan_variation_name'] = sprintf( '%s (%s %s %s)', $plan['name'], $this->amount, $this->currency, $phase_cadence['name'] );
$args_sub['card_name'] = empty( $this->fields[ $this->cc_field['id'] ]['cardname'] ) ? '' : sanitize_text_field( $this->fields[ $this->cc_field['id'] ]['cardname'] );
* Filter subscription payment arguments.
* @param array $args The subscription payment arguments.
* @param Process $process The Process instance.
return (array) apply_filters( 'wpforms_integrations_square_process_get_payment_args_subscription', $args_sub, $this );
* Validate plan before process.