make_post_sell/templates/cart_checkout.j2

- Added "Save PayPal for faster checkout" checkbox
    - Shows "PayPal saved" status for returning users
    - Sends save_paypal parameter to backend

make_post_sell/templates/billing.j2
    - Added PayPal management section
    - Shows connected status with PayPal logo
    - Added "Disconnect PayPal" button
This commit is contained in:
Groupr 2025-12-02 03:13:14 +00:00 • committed by Russell Ballestrini
parent 4a6f7ebb3a
commit e8e0e52f40
2 changed files with 176 additions and 0 deletions

View file

@ -38,6 +38,38 @@
</section>
{# PayPal Saved Payment Method Section #}
{% if paypal_enabled and paypal_user_shop %}
<section class="well" style="margin-top: 20px;">
<h3>PayPal Payment Method</h3>
{% if paypal_user_shop.has_saved_payment_method %}
<div style="padding: 15px; background-color: #f0f7ff; border-left: 4px solid #0070ba; border-radius: 4px; margin-top: 10px;">
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 8px;">
<svg width="24" height="24" viewBox="0 0 24 24" fill="#0070ba">
<path d="M20.067 8.478c.492.88.556 2.014.3 3.327-.74 3.806-3.276 5.12-6.514 5.12h-.5a.805.805 0 00-.794.68l-.04.22-.63 3.993-.032.17a.804.804 0 01-.794.679H7.72a.483.483 0 01-.477-.558L9.01 8.975a.964.964 0 01.951-.814h2.053c3.893 0 6.535 1.62 7.055 6.317z"/>
<path d="M10.964 8.975a.964.964 0 00-.95.814L8.247 21.891a.483.483 0 00.476.558h3.344c.336 0 .622-.243.678-.574l.028-.15.63-3.994.04-.218a.805.805 0 01.795-.68h.5c3.238 0 5.774-1.314 6.514-5.12.256-1.313.192-2.447-.3-3.327a3.918 3.918 0 00-1.172-1.241c-.67-.437-1.53-.695-2.527-.827-.27-.036-.549-.063-.837-.083h-.18z" opacity=".7"/>
</svg>
<span style="font-weight: 500; font-size: 16px;">PayPal Account Connected</span>
</div>
<p style="margin: 0 0 12px 0; color: #666; font-size: 14px;">
Your PayPal account is saved for quick checkout. Click the PayPal button at checkout to complete your purchase.
</p>
<form method="POST" action="/billing/disconnect-paypal" style="margin: 0;">
<input type="hidden" name="csrf_token" value="{{ request.session.get_csrf_token() }}">
<button type="submit" style="padding: 8px 16px; background-color: #fff; border: 1px solid #ccc; border-radius: 4px; cursor: pointer; font-size: 14px; color: #333;">
Disconnect PayPal
</button>
</form>
</div>
{% else %}
<p style="color: #666; margin-top: 10px;">
No PayPal account connected. Check the "Save PayPal" box during checkout to save your PayPal account for faster future purchases.
</p>
{% endif %}
</section>
{% endif %}
<center>
<a href="/cart" class="product-new-button mps-button">Review Order</a>
&nbsp;

View file

@ -56,6 +56,150 @@
</form>
{% endif %}
{# PayPal checkout button #}
{% if paypal_enabled and request.shop and request.shop.is_paypal_ready and cart.requires_payment %}
<br/>
{# Saved PayPal status or save checkbox #}
{% if paypal_user_shop and paypal_user_shop.has_saved_payment_method %}
{# User has PayPal saved #}
<div style="margin-bottom: 15px; padding: 12px; background-color: #f0f7ff; border-left: 4px solid: #0070ba; border-radius: 4px;">
<div style="display: flex; align-items: center; gap: 8px;">
<span style="font-size: 20px;">✓</span>
<span style="font-weight: 500; color: #0070ba;">PayPal saved for quick checkout</span>
</div>
<small style="display: block; margin-top: 4px; color: #666;">
Click the PayPal button below to complete your purchase
</small>
</div>
{% else %}
{# User does not have PayPal saved - show checkbox #}
<div style="margin-bottom: 15px;">
<label style="display: flex; align-items: center; gap: 8px; cursor: pointer;">
<input type="checkbox" id="save-paypal-checkbox" name="save_paypal" value="true" style="cursor: pointer;">
<span>💾 Save PayPal for faster checkout next time</span>
</label>
<small style="display: block; margin-left: 24px; color: #666; margin-top: 4px;">
You can manage saved payment methods in your account settings
</small>
</div>
{% endif %}
<div id="paypal-button-container"></div>
<script src="https://www.paypal.com/sdk/js?client-id={{ request.shop.paypal_client_id }}&currency=USD"></script>
<script>
// Store all order IDs for multi-shop support
var allPayPalOrderIds = [];
// Double-click protection flags
var isCreatingOrder = false;
var isProcessingApproval = false;
paypal.Buttons({
style: {
layout: 'vertical',
color: 'gold',
shape: 'rect',
label: 'paypal'
},
createOrder: function(data, actions) {
// Prevent duplicate order creation
if (isCreatingOrder) {
console.warn('PayPal order creation already in progress');
return Promise.reject(new Error('Order creation already in progress'));
}
isCreatingOrder = true;
// Check if user wants to save PayPal for future use
var savePayPalCheckbox = document.getElementById('save-paypal-checkbox');
var savePayPal = savePayPalCheckbox ? savePayPalCheckbox.checked : false;
return fetch("{{ request.route_url('paypal_create_order', cart_id=cart.id) }}?save_paypal=" + (savePayPal ? 'true' : 'false'), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': '{{ request.session.get_csrf_token() }}'
}
})
.then(response => response.json())
.then(data => {
if (data.error) {
alert('Error creating PayPal order: ' + data.error);
isCreatingOrder = false;
throw new Error(data.error);
}
// Handle both single order_id (old format) and order_ids array (new format)
if (data.order_ids) {
allPayPalOrderIds = data.order_ids;
isCreatingOrder = false;
// Return first order ID for PayPal SDK to process
return data.order_ids[0];
} else if (data.order_id) {
allPayPalOrderIds = [data.order_id];
isCreatingOrder = false;
return data.order_id;
}
})
.catch(error => {
isCreatingOrder = false;
throw error;
});
},
onApprove: function(data, actions) {
// Prevent duplicate submission after approval
if (isProcessingApproval) {
console.warn('PayPal approval already being processed');
return Promise.reject(new Error('Approval already being processed'));
}
isProcessingApproval = true;
// For multi-shop carts, we have multiple order IDs
// PayPal SDK only handles the first one, so we pass all IDs to backend
var orderIdsToSubmit = allPayPalOrderIds.join(',');
// Create form to submit PayPal order ID(s)
var form = document.createElement('form');
form.method = 'POST';
form.action = '{{ request.route_url('paypal_complete_checkout', cart_id=cart.id) }}';
var csrfInput = document.createElement('input');
csrfInput.type = 'hidden';
csrfInput.name = 'csrf_token';
csrfInput.value = '{{ request.session.get_csrf_token() }}';
form.appendChild(csrfInput);
var orderIdInput = document.createElement('input');
orderIdInput.type = 'hidden';
orderIdInput.name = 'paypal_order_id';
orderIdInput.value = orderIdsToSubmit;
form.appendChild(orderIdInput);
document.body.appendChild(form);
form.submit();
// Note: We don't reset isProcessingApproval because the page will redirect
// If form.submit() fails, the page stays and user can't retry anyway
},
onError: function(err) {
console.error('PayPal error:', err);
alert('PayPal payment failed. Please try again or use another payment method.');
// Reset flags on error so user can retry
isCreatingOrder = false;
isProcessingApproval = false;
},
onCancel: function(data) {
// Reset flags if user cancels PayPal flow
console.log('PayPal payment cancelled by user');
isCreatingOrder = false;
isProcessingApproval = false;
}
}).render('#paypal-button-container');
</script>
{% endif %}{# End PayPal enabled check #}
{# For free checkouts (e.g., with coupons), provide a simple confirmation button #}
{% if not cart.requires_payment %}
<form method="POST" action="{{ request.route_url('user_cart_complete_checkout', cart_id=cart.id) }}">