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 all 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 depend on the currency selected for Tickets Commerce and the currency supported by each payment method. If you select a payment provider other than Credit Cards, additional billing information fields will be displayed at 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' ),
'<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' ),
'<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
84 changes: 76 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,84 @@ 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');
field.nextElementSibling.classList.add( obj.selectors.hiddenElement.className() );
if (field.required && field.value === '') {
valid = false;
field.classList.add('error');
field.nextElementSibling.classList.remove( obj.selectors.hiddenElement.className() );
}
});

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() );
$('.tribe-tickets__commerce-checkout-section-header').removeClass( obj.selectors.hiddenElement.className() )
}, 2000);
}


/**
* Setup and initialize Stripe API.
Expand Down Expand Up @@ -604,7 +671,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
34 changes: 30 additions & 4 deletions src/resources/postcss/tickets-commerce/_checkout.pcss
Original file line number Diff line number Diff line change
Expand Up @@ -320,12 +320,30 @@

.tribe-tickets__form-field-description {
color: var(--tec-color-icon-primary-alt);
margin: var(--tec-spacer-1) 0;
margin: var(--tec-spacer-1) 0 0;

&.error {
color: red;
}
}

&.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-gap: 1rem;
grid-template-columns: 1fr 1fr;
}

.tribe-tickets__commerce-checkout-purchaser-info-title {
margin-bottom: var(--tec-spacer-3);
margin: var(--tec-spacer-3) 0;
}

.tribe-tickets__commerce-checkout-form-submit-button {
Expand All @@ -334,6 +352,15 @@
width: 100%;
}

.tribe-tickets__form-field-input-wrapper {

input::placeholder {
color: var(--tec-color-border-tertiary);
font-size: var(--tec-font-size-2);
opacity: 1;
}
}

/* -------------------------------------------------------------------------
* Theme Overrides - Twenty Twenty
* ------------------------------------------------------------------------- */
Expand All @@ -342,8 +369,7 @@

h1,
h2,
h3,
h4 {
h3 {
margin: initial;
}

Expand Down
3 changes: 1 addition & 2 deletions src/resources/postcss/tickets-commerce/_success.pcss
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,7 @@

h1,
h2,
h3,
h4 {
h3 {
margin: initial;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
*/
.event-tickets {
.tribe-tickets__commerce-checkout-section-header {
display: none;
margin-bottom: var(--tec-spacer-5);
}
}
13 changes: 9 additions & 4 deletions src/views/v2/commerce/checkout/gateways.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,20 @@
* @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;
}

?>
<div class="tribe-tickets__commerce-checkout-gateways">
<h4 class="tribe-common-h5 tribe-tickets__commerce-checkout-section-header">
<?php esc_html_e( 'Payment', 'event-tickets' ); ?>
<h4 class="tribe-common-h5 tribe-tickets__commerce-checkout-section-header tribe-common-a11y-hidden">
<?php esc_html_e( 'Payment info', 'event-tickets' ); ?>
</h4>
<?php
foreach ( $gateways as $gateway ) {
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>
65 changes: 65 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,65 @@
<?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"
placeholder="<?php esc_attr_e( 'Street address', 'event-tickets' ); ?>"
<?php tribe_classes( $field_classes ); ?>
required
/>
<div class="tribe-common-b3 tribe-tickets__form-field-description tribe-common-a11y-hidden error">
<?php esc_html_e( 'Your address is required', 'event-tickets' ); ?>
</div>
</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', 'event-tickets' ); ?>
</label>
<div class="tribe-tickets__form-field-input-wrapper">
<input
type="text"
id="tec-tc-purchaser-address2"
name="purchaser-address2"
placeholder="<?php esc_attr_e( 'Apt., suite, unit number, etc (optional)', 'event-tickets' ); ?>"
autocomplete="off"
<?php tribe_classes( $field_classes ); ?>
/>
<div class="tribe-common-b3 tribe-tickets__form-field-description tribe-common-a11y-hidden error"></div>
</div>
</div>
Loading
Loading