Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Address fields for Stripe payment #3125

Merged
merged 16 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ Check out our extensive [knowledgebase](https://evnt.is/18wm) for articles on us

= [TBD] TBD =

* Fix - Ensure that users fill in all required billing address fields when Stripe advanced payment methods are available. [ETP-934]
* Fix - Added missing global property for javascript undefined error `Uncaught TypeError: format is undefined`. This happened during ticket checkout.

= [5.13.0] 2024-07-22 =
Expand Down
2 changes: 1 addition & 1 deletion src/Tickets/Commerce/Gateways/Stripe/Settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ public function get_settings() {

$payment_methods_tooltip = sprintf(
// Translators: %1$s: Opening `<span>` tag. %2$s: The currency name. %3$s: Closing `</span>` tag. %4$s: Opening `<a>` tag for Stripe link. %5$s: Closing `</a>` tag.
__( '%1$sPayment methods available for %2$s%3$s.<br /><br /> The payment methods listed here are dependent on the currency selected for Tickets Commerce and the currency each payment method support. You can review the payment methods and their availablity for each currency on %4$sStripe\'s documentation%5$s.<br /><br />', 'event-tickets' ),
__( '%1$sPayment methods available for %2$s%3$s.<br /><br /> The payment methods listed here are dependent on the currency selected for Tickets Commerce and the currency each payment method support. If you select a payment provider outside of Credit Cards, additional billing information fields will display on checkout as required by Stripe. You can review the payment methods and their availablity for each currency on %4$sStripe\'s documentation%5$s.<br /><br />', 'event-tickets' ),
pattihis marked this conversation as resolved.
Show resolved Hide resolved
'<span class="tec-tickets__admin-settings-tickets-commerce-gateway-currency">',
$currency_name,
'</span>',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,18 @@
?>
</div>

<div class="tec-tickets__admin-settings-tickets-commerce-stripe-modal-content-section">
<strong><?php esc_html_e( 'Billing Fields', 'event-tickets' ); ?></strong> &mdash;
<?php
printf(
// Translators: %1$s: opening `a` tag to the knowledge base article. %2$s: closing `a` tag.
esc_html__( 'If you use certain payment providers with Stripe including Afterpay, Klarna or Clearpay additional Billing Fields will be displayed at checkout as required by Stripe. %1$sLearn More%2$s.', 'event-tickets' ),
pattihis marked this conversation as resolved.
Show resolved Hide resolved
'<a href="https://theeventscalendar.com/knowledgebase/k/pci-compliance/" target="_blank" rel="noopener noreferrer" class="tribe-common-anchor-alt">',
'</a>'
);
?>
</div>


<div class="tec-tickets__admin-modal-buttons">

Expand Down
81 changes: 73 additions & 8 deletions src/resources/js/commerce/gateway/stripe/checkout.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ tribe.tickets.commerce.gateway.stripe.checkout = {};
cardErrors: '#tec-tc-gateway-stripe-errors',
paymentElement: '#tec-tc-gateway-stripe-payment-element',
paymentMessage: '#tec-tc-gateway-stripe-payment-message',
infoForm: '.tribe-tickets__commerce-checkout-purchaser-info-wrapper',
renderButton: '#tec-tc-gateway-stripe-render-payment',
submitButton: '#tec-tc-gateway-stripe-checkout-button',
hiddenElement: '.tribe-common-a11y-hidden'
};
Expand Down Expand Up @@ -504,19 +506,81 @@ tribe.tickets.commerce.gateway.stripe.checkout = {};
* @since 5.3.0
*/
obj.setupPaymentElement = () => {
// Only if we don't have the address fields to collect
if ( 0 === $('#tec-tc-gateway-stripe-render-payment').length ) {
const walletSettings = obj.getWallets();
// Instantiate the PaymentElement
obj.paymentElement = obj.stripeElements.create( 'payment', {
fields: {
name: 'auto',
email: 'auto',
phone: 'auto',
address: 'auto'
},
wallets: walletSettings
} );
obj.paymentElement.mount( obj.selectors.paymentElement );
}
};

obj.renderPayment = () => {
const form = $( obj.selectors.infoForm );
const fields = form.find('input, select');
let valid = true;
fields.each((index, field) => {
field.classList.remove('error');
if (field.required && field.value === '') {
valid = false;
field.classList.add('error');
}
});

if (!valid) {
return;
}

$( obj.selectors.renderButton ).addClass( obj.selectors.hiddenElement.className() );
form.children('select, input').prop( 'disabled', true );
form.addClass( 'disabled' );
const walletSettings = obj.getWallets();
// Instantiate the PaymentElement
obj.paymentElement = obj.stripeElements.create( 'payment', {
fields: {
name: 'auto',
email: 'auto',
phone: 'auto',
address: 'auto'
defaultValues: {
billingDetails: {
name: $('#tec-tc-purchaser-name').val(),
email: $('#tec-tc-purchaser-email').val(),
phone: '',
address: {
line1: $('#tec-tc-purchaser-address1').val(),
line2: $('#tec-tc-purchaser-address2').val(),
city: $('#tec-tc-purchaser-city').val(),
state: $('tec-tc-purchaser-state').val(),
country: $('#tec-tc-purchaser-country').val(),
postal_code: $('#tec-tc-purchaser-zip').val()
}
},
shippingDetails: {
name: $('#tec-tc-purchaser-name').val(),
email: $('#tec-tc-purchaser-email').val(),
phone: '',
address: {
line1: $('#tec-tc-purchaser-address1').val(),
line2: $('#tec-tc-purchaser-address2').val(),
city: $('#tec-tc-purchaser-city').val(),
state: $('tec-tc-purchaser-state').val(),
country: $('#tec-tc-purchaser-country').val(),
postal_code: $('#tec-tc-purchaser-zip').val()
}
},
},
wallets: walletSettings
} );
obj.paymentElement.mount( obj.selectors.paymentElement );
};
setTimeout(() => {
$('.tribe-tickets__commerce-checkout-gateways').get(0).scrollIntoView({behavior: 'smooth'});
$( obj.selectors.submitButton ).removeClass( obj.selectors.hiddenElement.className() );
}, 2000);
}


/**
* Setup and initialize Stripe API.
Expand Down Expand Up @@ -604,7 +668,8 @@ tribe.tickets.commerce.gateway.stripe.checkout = {};
* @since 5.3.0
*/
obj.bindEvents = () => {
$( obj.selectors.submitButton ).on( 'click', obj.handlePayment );
$( document ).on( 'click', obj.selectors.renderButton, obj.renderPayment );
$( document ).on( 'click', obj.selectors.submitButton, obj.handlePayment );
};

/**
Expand Down
14 changes: 14 additions & 0 deletions src/resources/postcss/tickets-commerce/_checkout.pcss
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,20 @@
color: var(--tec-color-icon-primary-alt);
margin: var(--tec-spacer-1) 0;
}

&.disabled {
opacity: 0.5;
}
}

.tribe-tickets__commerce-checkout-purchaser-info-form-field.error {
border: 1px solid red!important;
}

.tribe-tickets__commerce-checkout-address-wrapper {
display: grid;
grid-template-columns: 1fr 1fr;
pattihis marked this conversation as resolved.
Show resolved Hide resolved
grid-gap: 1rem;
}

.tribe-tickets__commerce-checkout-purchaser-info-title {
Expand Down
9 changes: 7 additions & 2 deletions src/views/v2/commerce/checkout/gateways.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,13 @@
* @var int $gateways_connected [Global] The number of connected gateways.
*/

// Bail if user needs to login, the cart is empty or if there are no active gateways.
if ( $must_login || empty( $items ) || ! tribe_is_truthy( $gateways_active ) ) {
// Bail if the cart is empty or if there are no active gateways.
if ( empty( $items ) || ! tribe_is_truthy( $gateways_active ) ) {
return;
}

// Bail if user needs to login, but is not logged in.
if ( $must_login && ! is_user_logged_in() ) {
return;
}

Expand Down
50 changes: 46 additions & 4 deletions src/views/v2/commerce/checkout/purchaser-info.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,55 @@
* @var bool $must_login Global] Whether login is required to buy tickets or not.
*/

if ( is_user_logged_in() || $must_login || empty( $items ) ) {
// Bail if the cart is empty.
if ( empty( $items ) ) {
return;
}

// Bail if user needs to login, but is not logged in.
if ( $must_login && ! is_user_logged_in() ) {
return;
}
?>

$info_title = __( 'Purchaser info', 'event-tickets' );
$show_address = false;
foreach ( $gateways as $gateway ) {
// Check if Stripe is active and enabled.
if (
'stripe' === $gateway::get_key()
&& $gateway::is_enabled()
&& $gateway::is_active()
) {
$payment_methods = ( new TEC\Tickets\Commerce\Gateways\Stripe\Merchant() )->get_payment_method_types();
// If more than one payment method, or if only one but not a card, we need to show the address fields.
if (
1 < count( $payment_methods )
|| (
1 === count( $payment_methods )
&& 'card' !== $payment_methods[0]
)
) {
$info_title = __( 'Billing info', 'event-tickets' );
$show_address = true;
}
}
}

?>
<div class="tribe-tickets__form tribe-tickets__commerce-checkout-purchaser-info-wrapper tribe-common-b2">
<h4 class="tribe-common-h5 tribe-tickets__commerce-checkout-purchaser-info-title"><?php esc_html_e( 'Purchaser info', 'event-tickets' ); ?></h4>
<?php $this->template( 'checkout/purchaser-info/name' ); ?>
<h4 class="tribe-common-h5 tribe-tickets__commerce-checkout-purchaser-info-title"><?php echo esc_html( $info_title ); ?></h4>
<?php $this->template( 'checkout/purchaser-info/name', [ 'show_address' => $show_address ] ); ?>
<?php $this->template( 'checkout/purchaser-info/email' ); ?>
<?php if ( $show_address ) : ?>
<?php $this->template( 'checkout/purchaser-info/address' ); ?>
<div class="tribe-tickets__commerce-checkout-address-wrapper">
<?php $this->template( 'checkout/purchaser-info/city' ); ?>
<?php $this->template( 'checkout/purchaser-info/state' ); ?>
<?php $this->template( 'checkout/purchaser-info/zip' ); ?>
<?php $this->template( 'checkout/purchaser-info/country' ); ?>
</div>
<button id="tec-tc-gateway-stripe-render-payment" class="tribe-common-c-btn tribe-tickets__commerce-checkout-form-submit-button">
<?php esc_html_e( 'Proceed to payment', 'event-tickets' ); ?>
</button>
<?php endif; ?>
</div>
59 changes: 59 additions & 0 deletions src/views/v2/commerce/checkout/purchaser-info/address.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php
/**
* Tickets Commerce: Checkout Page Purchaser Info Address Fields
*
* Override this template in your own theme by creating a file at:
* [your-theme]/tribe/tickets/v2/commerce/checkout/purchaser-info/address.php
*
* See more documentation about our views templating system.
*
* @link https://evnt.is/1amp Help article for RSVP & Ticket template files.
*
* @since TBD
*
* @version TBD
*
* @var \Tribe__Template $this [Global] Template object.
*/

$label_classes = [
'tribe-tickets__form-field-label',
'tribe-tickets__commerce-checkout-purchaser-info-address-field-label',
];

$field_classes = [
'tribe-tickets__commerce-checkout-purchaser-info-form-field',
'tribe-tickets__commerce-checkout-purchaser-info-form-field-address',
'tribe-common-form-control-text__input',
'tribe-tickets__form-field-input',
];
?>
<div class="tribe-tickets__commerce-checkout-purchaser-info-field tribe-tickets__form-field tribe-tickets__form-field--text">
<label for="tec-tc-purchaser-address1" <?php tribe_classes( $label_classes ); ?>>
<?php esc_html_e( 'Address Line 1:', 'event-tickets' ); ?>
</label>
<div class="tribe-tickets__form-field-input-wrapper">
<input
type="text"
id="tec-tc-purchaser-address1"
name="purchaser-address1"
autocomplete="off"
<?php tribe_classes( $field_classes ); ?>
required
/>
</div>
</div>
<div class="tribe-tickets__commerce-checkout-purchaser-info-field tribe-tickets__form-field tribe-tickets__form-field--text">
<label for="tec-tc-purchaser-address2" <?php tribe_classes( $label_classes ); ?>>
<?php esc_html_e( 'Address Line 2 (optional):', 'event-tickets' ); ?>
</label>
<div class="tribe-tickets__form-field-input-wrapper">
<input
type="text"
id="tec-tc-purchaser-address2"
name="purchaser-address2"
autocomplete="off"
<?php tribe_classes( $field_classes ); ?>
/>
</div>
</div>
47 changes: 47 additions & 0 deletions src/views/v2/commerce/checkout/purchaser-info/city.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php
/**
* Tickets Commerce: Checkout Page Purchaser Info City Field
*
* Override this template in your own theme by creating a file at:
* [your-theme]/tribe/tickets/v2/commerce/checkout/purchaser-info/city.php
*
* See more documentation about our views templating system.
*
* @link https://evnt.is/1amp Help article for RSVP & Ticket template files.
*
* @since TBD
*
* @version TBD
*
* @var \Tribe__Template $this [Global] Template object.
*/

$label_classes = [
'tribe-tickets__form-field-label',
'tribe-tickets__commerce-checkout-purchaser-info-city-field-label',
];

$field_classes = [
'tribe-common-b2',
'tribe-tickets__commerce-checkout-purchaser-info-form-field',
'tribe-tickets__commerce-checkout-purchaser-info-form-field-city',
'tribe-common-form-control-text__input',
'tribe-tickets__form-field-input',
];
?>
<div class="tribe-tickets__commerce-checkout-purchaser-info-field tribe-tickets__form-field tribe-tickets__form-field--city">
<label for="tec-tc-purchaser-city" <?php tribe_classes( $label_classes ); ?>>
<?php esc_html_e( 'City:', 'event-tickets' ); ?>
</label>

<div class="tribe-tickets__form-field-input-wrapper">
<input
type="text"
id="tec-tc-purchaser-city"
name="purchaser-city"
autocomplete="off"
<?php tribe_classes( $field_classes ); ?>
required
/>
</div>
</div>
Loading
Loading