You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
748 lines
27 KiB
748 lines
27 KiB
{% extends "base.html" %}
|
|
|
|
{% block title %}Add Payment Method - Plutus{% endblock %}
|
|
|
|
{% block content %}
|
|
<nav class="breadcrumb" aria-label="breadcrumbs">
|
|
<ul>
|
|
<li><a href="{{ url_for('main.index') }}">Dashboard</a></li>
|
|
<li><a href="{{ url_for('main.single_payments_list') }}">Single Payments</a></li>
|
|
<li class="is-active"><a href="#" aria-current="page">Add Payment Method</a></li>
|
|
</ul>
|
|
</nav>
|
|
|
|
<div class="level">
|
|
<div class="level-left">
|
|
<div>
|
|
<h1 class="title">Add Payment Method</h1>
|
|
<p class="subtitle">Add credit cards or BECS Direct Debit to customer accounts</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Add Payment Method Form -->
|
|
<div class="box">
|
|
<!-- Step 1: Enter Splynx ID -->
|
|
<div id="step1" class="payment-step">
|
|
<h2 class="title is-4">
|
|
<span class="icon"><i class="fas fa-search"></i></span>
|
|
Customer Lookup
|
|
</h2>
|
|
|
|
<div class="field">
|
|
<label class="label" for="lookup_splynx_id">Splynx Customer ID</label>
|
|
<div class="control">
|
|
<input class="input" type="number" id="lookup_splynx_id" placeholder="Enter customer ID" required>
|
|
</div>
|
|
<p class="help">Enter the Splynx customer ID to fetch customer details</p>
|
|
</div>
|
|
|
|
<!-- Loading State -->
|
|
<div id="loading" class="has-text-centered py-5 is-hidden">
|
|
<div class="spinner"></div>
|
|
<p class="mt-3">Fetching customer details...</p>
|
|
</div>
|
|
|
|
<!-- Error State -->
|
|
<div id="customerError" class="notification is-danger is-hidden">
|
|
<span class="icon"><i class="fas fa-exclamation-triangle"></i></span>
|
|
<span id="errorMessage">Customer not found or error occurred</span>
|
|
</div>
|
|
|
|
<div class="field is-grouped">
|
|
<div class="control">
|
|
<button class="button is-primary" id="nextBtn" onclick="fetchCustomerDetails()">
|
|
<span class="icon"><i class="fas fa-arrow-right"></i></span>
|
|
<span>Next</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Step 2: Confirm Customer & Select Payment Method Type -->
|
|
<div id="step2" class="payment-step is-hidden">
|
|
<h2 class="title is-4">
|
|
<span class="icon"><i class="fas fa-user-check"></i></span>
|
|
Confirm Customer & Select Payment Method Type
|
|
</h2>
|
|
|
|
<div class="box has-background-light mb-5">
|
|
<h3 class="subtitle is-5">Customer Information</h3>
|
|
<div id="customerDetails">
|
|
<!-- Customer details will be populated here -->
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Current Payment Methods -->
|
|
<div class="box has-background-info-light mb-5">
|
|
<h3 class="subtitle is-5">
|
|
<span class="icon"><i class="fas fa-credit-card"></i></span>
|
|
Current Payment Methods
|
|
</h3>
|
|
<div id="currentPaymentMethods">
|
|
<div class="has-text-centered py-4">
|
|
<div class="spinner"></div>
|
|
<p class="mt-3">Loading current payment methods...</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="field">
|
|
<label class="label">Payment Method Type</label>
|
|
<div class="control">
|
|
<div class="columns">
|
|
<div class="column">
|
|
<div class="box payment-type-card" onclick="selectPaymentType('card')" id="cardOption">
|
|
<div class="has-text-centered">
|
|
<span class="icon is-large has-text-info">
|
|
<i class="fas fa-credit-card fa-2x"></i>
|
|
</span>
|
|
<h4 class="title is-5 mt-3">Credit/Debit Card</h4>
|
|
<p class="content">Add a new credit or debit card</p>
|
|
<ul class="content is-small">
|
|
<li>Domestic cards: 1.7% + $0.30</li>
|
|
<li>International cards: 3.5% + $0.30</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="column">
|
|
<div class="box payment-type-card" onclick="selectPaymentType('au_becs_debit')" id="becsOption">
|
|
<div class="has-text-centered">
|
|
<span class="icon is-large has-text-success">
|
|
<i class="fas fa-university fa-2x"></i>
|
|
</span>
|
|
<h4 class="title is-5 mt-3">BECS Direct Debit</h4>
|
|
<p class="content">Add Australian bank account</p>
|
|
<ul class="content is-small">
|
|
<li>1.0% + $0.30 (capped at $3.50)</li>
|
|
<li>Lower fees than cards</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="field is-grouped">
|
|
<div class="control">
|
|
<button class="button is-light" onclick="goBackToStep1()">
|
|
<span class="icon"><i class="fas fa-arrow-left"></i></span>
|
|
<span>Back</span>
|
|
</button>
|
|
</div>
|
|
<div class="control">
|
|
<button class="button is-primary" id="continueBtn" onclick="setupPaymentMethod()" disabled>
|
|
<span class="icon"><i class="fas fa-arrow-right"></i></span>
|
|
<span>Continue</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Step 3: Payment Method Collection -->
|
|
<div id="step3" class="payment-step is-hidden">
|
|
<h2 class="title is-4">
|
|
<span class="icon"><i class="fas fa-plus-circle"></i></span>
|
|
Add Payment Method
|
|
</h2>
|
|
|
|
<div class="box has-background-light mb-5">
|
|
<h3 class="subtitle is-5">Selected Type</h3>
|
|
<div id="selectedTypeDisplay">
|
|
<!-- Selected payment method type will be shown here -->
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Stripe Elements Container -->
|
|
<div class="field">
|
|
<label class="label" id="paymentElementLabel">Payment Details</label>
|
|
<div class="control">
|
|
<div id="payment-element" class="stripe-element">
|
|
<!-- Stripe Elements will be mounted here -->
|
|
</div>
|
|
</div>
|
|
<div id="payment-element-errors" role="alert" class="help is-danger is-hidden"></div>
|
|
</div>
|
|
|
|
<!-- BECS Mandate Agreement (only shown for BECS) -->
|
|
<div id="becsMandate" class="notification is-info is-hidden">
|
|
<div class="content">
|
|
<h4>Direct Debit Request Service Agreement</h4>
|
|
<p>By providing your bank account details and confirming this payment, you acknowledge that:</p>
|
|
<ul>
|
|
<li>You have read and agree to the Direct Debit Request Service Agreement</li>
|
|
<li>You authorize debits to your account according to the arrangement outlined</li>
|
|
<li>This authorization will remain in effect until cancelled by you</li>
|
|
</ul>
|
|
<label class="checkbox">
|
|
<input type="checkbox" id="becsAgreement">
|
|
I agree to the Direct Debit Request Service Agreement
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Set as Default Option -->
|
|
<div class="field">
|
|
<div class="control">
|
|
<label class="checkbox">
|
|
<input type="checkbox" id="setAsDefault" checked>
|
|
Set as default payment method for this customer
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="field is-grouped">
|
|
<div class="control">
|
|
<button class="button is-light" onclick="goBackToStep2()">
|
|
<span class="icon"><i class="fas fa-arrow-left"></i></span>
|
|
<span>Back</span>
|
|
</button>
|
|
</div>
|
|
<div class="control">
|
|
<button class="button is-primary" id="savePaymentMethodBtn" onclick="savePaymentMethod()">
|
|
<span class="icon"><i class="fas fa-save"></i></span>
|
|
<span>Save Payment Method</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Processing State -->
|
|
<div id="processingPayment" class="notification is-info is-hidden">
|
|
<div class="has-text-centered py-4">
|
|
<div class="spinner"></div>
|
|
<p class="mt-3">Processing payment method...</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Step 4: Success -->
|
|
<div id="step4" class="payment-step is-hidden">
|
|
<h2 class="title is-4 has-text-success">
|
|
<span class="icon"><i class="fas fa-check-circle"></i></span>
|
|
Payment Method Added Successfully
|
|
</h2>
|
|
|
|
<div class="box has-background-success-light">
|
|
<div id="successDetails">
|
|
<!-- Success details will be populated here -->
|
|
</div>
|
|
</div>
|
|
|
|
<div class="field is-grouped">
|
|
<div class="control">
|
|
<button class="button is-success" onclick="addAnotherPaymentMethod()">
|
|
<span class="icon"><i class="fas fa-plus"></i></span>
|
|
<span>Add Another Payment Method</span>
|
|
</button>
|
|
</div>
|
|
<div class="control">
|
|
<a class="button is-info" href="{{ url_for('main.single_payments_list') }}">
|
|
<span class="icon"><i class="fas fa-list"></i></span>
|
|
<span>View Payments</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Include Stripe.js -->
|
|
<script src="https://js.stripe.com/v3/"></script>
|
|
|
|
<script>
|
|
// Global variables
|
|
let currentCustomer = null;
|
|
let selectedPaymentType = null;
|
|
let stripe = null;
|
|
let elements = null;
|
|
let paymentElement = null;
|
|
let setupIntentClientSecret = null;
|
|
|
|
// Initialize Stripe (we'll get the publishable key from the backend)
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// We'll initialize Stripe when we need it in step 3
|
|
});
|
|
|
|
function fetchCustomerDetails() {
|
|
const splynxId = document.getElementById('lookup_splynx_id').value;
|
|
|
|
if (!splynxId) {
|
|
showError('Please enter a Splynx ID');
|
|
return;
|
|
}
|
|
|
|
showLoading(true);
|
|
hideError();
|
|
|
|
// Use existing API endpoint to get Stripe customer ID
|
|
fetch(`/api/stripe-customer-id/${splynxId}`)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
// Get Splynx customer details
|
|
return fetch(`/api/splynx/${splynxId}`)
|
|
.then(response => response.json())
|
|
.then(splynxData => {
|
|
// Combine the data
|
|
const combinedData = {
|
|
success: true,
|
|
splynx_id: splynxId,
|
|
stripe_customer_id: data.stripe_customer_id,
|
|
customer_name: `${splynxData.name || ''} ${splynxData.lastname || ''}`.trim(),
|
|
customer_email: splynxData.email || '',
|
|
splynx_data: splynxData
|
|
};
|
|
|
|
currentCustomer = combinedData;
|
|
displayCustomerDetails(combinedData);
|
|
loadCurrentPaymentMethods(data.stripe_customer_id);
|
|
showStep(2);
|
|
showLoading(false);
|
|
});
|
|
} else {
|
|
showLoading(false);
|
|
showError(data.error || 'Customer not found or no Stripe customer ID available');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
showLoading(false);
|
|
showError('Error fetching customer details: ' + error.message);
|
|
});
|
|
}
|
|
|
|
function displayCustomerDetails(customer) {
|
|
const html = `
|
|
<div class="columns">
|
|
<div class="column">
|
|
<strong>Name:</strong> ${customer.customer_name || 'N/A'}<br>
|
|
<strong>Email:</strong> ${customer.customer_email || 'N/A'}<br>
|
|
<strong>Splynx ID:</strong> ${customer.splynx_id}
|
|
</div>
|
|
<div class="column">
|
|
<strong>Stripe Customer ID:</strong> ${customer.stripe_customer_id}<br>
|
|
<strong>Status:</strong> <span class="tag is-success">Active</span>
|
|
</div>
|
|
</div>
|
|
`;
|
|
document.getElementById('customerDetails').innerHTML = html;
|
|
}
|
|
|
|
function loadCurrentPaymentMethods(stripeCustomerId) {
|
|
fetch(`/api/stripe-payment-methods/${stripeCustomerId}`)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
displayCurrentPaymentMethods(data.payment_methods);
|
|
} else {
|
|
document.getElementById('currentPaymentMethods').innerHTML =
|
|
'<p class="has-text-danger">Error loading payment methods: ' + (data.error || 'Unknown error') + '</p>';
|
|
}
|
|
})
|
|
.catch(error => {
|
|
document.getElementById('currentPaymentMethods').innerHTML =
|
|
'<p class="has-text-danger">Error loading payment methods: ' + error.message + '</p>';
|
|
});
|
|
}
|
|
|
|
function displayCurrentPaymentMethods(methods) {
|
|
const container = document.getElementById('currentPaymentMethods');
|
|
|
|
if (!methods || methods.length === 0) {
|
|
container.innerHTML = '<p class="has-text-grey">No payment methods found for this customer.</p>';
|
|
return;
|
|
}
|
|
|
|
let html = '<div class="columns is-multiline">';
|
|
methods.forEach(method => {
|
|
let methodInfo = '';
|
|
let icon = '';
|
|
|
|
if (method.type === 'card' && method.card) {
|
|
icon = 'fas fa-credit-card';
|
|
methodInfo = `${method.card.brand.toUpperCase()} ending in ${method.card.last4}`;
|
|
} else if (method.type === 'au_becs_debit' && method.au_becs_debit) {
|
|
icon = 'fas fa-university';
|
|
methodInfo = `Bank account ${method.au_becs_debit.bsb_number} ending in ${method.au_becs_debit.last4}`;
|
|
} else {
|
|
icon = 'fas fa-question-circle';
|
|
methodInfo = `${method.type} payment method`;
|
|
}
|
|
|
|
html += `
|
|
<div class="column is-half">
|
|
<div class="box is-small">
|
|
<div class="media">
|
|
<div class="media-left">
|
|
<span class="icon has-text-info">
|
|
<i class="${icon}"></i>
|
|
</span>
|
|
</div>
|
|
<div class="media-content">
|
|
<p class="is-size-6">${methodInfo}</p>
|
|
<p class="is-size-7 has-text-grey">Added: ${new Date(method.created * 1000).toLocaleDateString()}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
});
|
|
html += '</div>';
|
|
|
|
container.innerHTML = html;
|
|
}
|
|
|
|
function selectPaymentType(type) {
|
|
selectedPaymentType = type;
|
|
|
|
// Update UI
|
|
document.querySelectorAll('.payment-type-card').forEach(card => {
|
|
card.classList.remove('has-background-primary-light', 'has-border-primary');
|
|
});
|
|
|
|
const selectedCard = document.getElementById(type === 'card' ? 'cardOption' : 'becsOption');
|
|
selectedCard.classList.add('has-background-primary-light', 'has-border-primary');
|
|
|
|
document.getElementById('continueBtn').disabled = false;
|
|
}
|
|
|
|
function setupPaymentMethod() {
|
|
if (!selectedPaymentType) {
|
|
showError('Please select a payment method type');
|
|
return;
|
|
}
|
|
|
|
// Update selected type display
|
|
const typeDisplay = selectedPaymentType === 'card' ?
|
|
'<span class="icon"><i class="fas fa-credit-card"></i></span> Credit/Debit Card' :
|
|
'<span class="icon"><i class="fas fa-university"></i></span> BECS Direct Debit';
|
|
document.getElementById('selectedTypeDisplay').innerHTML = typeDisplay;
|
|
|
|
// Show/hide BECS mandate
|
|
if (selectedPaymentType === 'au_becs_debit') {
|
|
document.getElementById('becsMandate').classList.remove('is-hidden');
|
|
document.getElementById('paymentElementLabel').textContent = 'Bank Account Details';
|
|
} else {
|
|
document.getElementById('becsMandate').classList.add('is-hidden');
|
|
document.getElementById('paymentElementLabel').textContent = 'Card Details';
|
|
}
|
|
|
|
showStep(3);
|
|
initializeStripeElements();
|
|
}
|
|
|
|
function initializeStripeElements() {
|
|
console.log('Initializing Stripe elements for payment type:', selectedPaymentType);
|
|
console.log('Customer:', currentCustomer);
|
|
|
|
// Get Stripe publishable key and create setup intent
|
|
fetch('/api/create-setup-intent', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
stripe_customer_id: currentCustomer.stripe_customer_id,
|
|
payment_method_types: [selectedPaymentType]
|
|
})
|
|
})
|
|
.then(response => {
|
|
console.log('Setup intent response status:', response.status);
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
console.log('Setup intent response data:', data);
|
|
if (data.success) {
|
|
setupIntentClientSecret = data.client_secret;
|
|
console.log('Client secret received:', data.client_secret.substring(0, 20) + '...');
|
|
initializeStripe(data.stripe_publishable_key, data.client_secret);
|
|
} else {
|
|
console.error('Setup intent failed:', data.error);
|
|
showError('Failed to initialize payment method setup: ' + data.error);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Setup intent error:', error);
|
|
showError('Error initializing setup: ' + error.message);
|
|
});
|
|
}
|
|
|
|
function initializeStripe(publishableKey, clientSecret) {
|
|
console.log('Initializing Stripe with publishable key:', publishableKey.substring(0, 12) + '...');
|
|
console.log('Selected payment type:', selectedPaymentType);
|
|
|
|
stripe = Stripe(publishableKey);
|
|
|
|
// Create elements with specific appearance and payment method options
|
|
const appearance = {
|
|
theme: 'stripe',
|
|
variables: {
|
|
colorPrimary: '#3273dc',
|
|
colorBackground: '#ffffff',
|
|
colorText: '#30313d',
|
|
colorDanger: '#df1b41',
|
|
fontFamily: 'Ideal Sans, system-ui, sans-serif',
|
|
spacingUnit: '2px',
|
|
borderRadius: '4px'
|
|
}
|
|
};
|
|
|
|
elements = stripe.elements({
|
|
clientSecret: clientSecret,
|
|
appearance: appearance
|
|
});
|
|
|
|
// Configure payment element with specific payment method types
|
|
const paymentElementOptions = {
|
|
layout: 'tabs',
|
|
paymentMethodOrder: selectedPaymentType === 'card'
|
|
? ['card']
|
|
: ['au_becs_debit'],
|
|
fields: {
|
|
billingDetails: {
|
|
name: 'auto',
|
|
email: 'auto'
|
|
}
|
|
}
|
|
};
|
|
|
|
console.log('Creating payment element with options:', paymentElementOptions);
|
|
|
|
paymentElement = elements.create('payment', paymentElementOptions);
|
|
|
|
console.log('Mounting payment element to #payment-element');
|
|
const mountElement = document.getElementById('payment-element');
|
|
if (mountElement) {
|
|
console.log('Mount element found:', mountElement);
|
|
try {
|
|
paymentElement.mount('#payment-element');
|
|
console.log('Payment element mounted successfully');
|
|
} catch (error) {
|
|
console.error('Error mounting payment element:', error);
|
|
showError('Error mounting payment form: ' + error.message);
|
|
}
|
|
} else {
|
|
console.error('Mount element #payment-element not found');
|
|
showError('Payment form container not found');
|
|
}
|
|
|
|
paymentElement.on('change', function(event) {
|
|
console.log('Payment element change event:', event);
|
|
const errorElement = document.getElementById('payment-element-errors');
|
|
if (event.error) {
|
|
errorElement.textContent = event.error.message;
|
|
errorElement.classList.remove('is-hidden');
|
|
} else {
|
|
errorElement.textContent = '';
|
|
errorElement.classList.add('is-hidden');
|
|
}
|
|
});
|
|
|
|
paymentElement.on('ready', function() {
|
|
console.log('Payment element is ready and visible');
|
|
});
|
|
|
|
paymentElement.on('loaderror', function(event) {
|
|
console.error('Payment element load error:', event);
|
|
showError('Error loading payment form: ' + event.error.message);
|
|
});
|
|
}
|
|
|
|
function savePaymentMethod() {
|
|
// Validate BECS agreement if needed
|
|
if (selectedPaymentType === 'au_becs_debit') {
|
|
const becsAgreement = document.getElementById('becsAgreement');
|
|
if (!becsAgreement.checked) {
|
|
showError('Please agree to the Direct Debit Request Service Agreement');
|
|
return;
|
|
}
|
|
}
|
|
|
|
const saveBtn = document.getElementById('savePaymentMethodBtn');
|
|
saveBtn.disabled = true;
|
|
saveBtn.innerHTML = '<span class="icon"><i class="fas fa-spinner fa-spin"></i></span><span>Processing...</span>';
|
|
|
|
document.getElementById('processingPayment').classList.remove('is-hidden');
|
|
|
|
// Confirm the setup intent
|
|
stripe.confirmSetup({
|
|
elements,
|
|
confirmParams: {
|
|
return_url: window.location.origin + '/single-payments/add-payment-method/success',
|
|
},
|
|
redirect: 'if_required'
|
|
}).then(function(result) {
|
|
if (result.error) {
|
|
// Handle error
|
|
showError('Payment method setup failed: ' + result.error.message);
|
|
saveBtn.disabled = false;
|
|
saveBtn.innerHTML = '<span class="icon"><i class="fas fa-save"></i></span><span>Save Payment Method</span>';
|
|
document.getElementById('processingPayment').classList.add('is-hidden');
|
|
} else {
|
|
// Setup succeeded
|
|
handleSetupSuccess(result.setupIntent);
|
|
}
|
|
});
|
|
}
|
|
|
|
function handleSetupSuccess(setupIntent) {
|
|
const setAsDefault = document.getElementById('setAsDefault').checked;
|
|
|
|
// Attach payment method and optionally set as default
|
|
fetch('/api/finalize-payment-method', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
setup_intent_id: setupIntent.id,
|
|
stripe_customer_id: currentCustomer.stripe_customer_id,
|
|
set_as_default: setAsDefault,
|
|
splynx_id: currentCustomer.splynx_id
|
|
})
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
document.getElementById('processingPayment').classList.add('is-hidden');
|
|
|
|
if (data.success) {
|
|
displaySuccess(data);
|
|
showStep(4);
|
|
} else {
|
|
showError('Failed to finalize payment method: ' + data.error);
|
|
const saveBtn = document.getElementById('savePaymentMethodBtn');
|
|
saveBtn.disabled = false;
|
|
saveBtn.innerHTML = '<span class="icon"><i class="fas fa-save"></i></span><span>Save Payment Method</span>';
|
|
}
|
|
})
|
|
.catch(error => {
|
|
document.getElementById('processingPayment').classList.add('is-hidden');
|
|
showError('Error finalizing setup: ' + error.message);
|
|
const saveBtn = document.getElementById('savePaymentMethodBtn');
|
|
saveBtn.disabled = false;
|
|
saveBtn.innerHTML = '<span class="icon"><i class="fas fa-save"></i></span><span>Save Payment Method</span>';
|
|
});
|
|
}
|
|
|
|
function displaySuccess(data) {
|
|
const paymentMethod = data.payment_method;
|
|
let methodInfo = '';
|
|
|
|
if (paymentMethod.type === 'card' && paymentMethod.card) {
|
|
methodInfo = `${paymentMethod.card.brand.toUpperCase()} ending in ${paymentMethod.card.last4}`;
|
|
} else if (paymentMethod.type === 'au_becs_debit' && paymentMethod.au_becs_debit) {
|
|
methodInfo = `Bank account ${paymentMethod.au_becs_debit.bsb_number} ending in ${paymentMethod.au_becs_debit.last4}`;
|
|
}
|
|
|
|
const html = `
|
|
<div class="content">
|
|
<h4>Payment Method Added</h4>
|
|
<p><strong>Type:</strong> ${methodInfo}</p>
|
|
<p><strong>Customer:</strong> ${currentCustomer.customer_name} (${currentCustomer.customer_email})</p>
|
|
<p><strong>Set as Default:</strong> ${data.is_default ? 'Yes' : 'No'}</p>
|
|
<p><strong>Payment Method ID:</strong> <code>${paymentMethod.id}</code></p>
|
|
</div>
|
|
`;
|
|
document.getElementById('successDetails').innerHTML = html;
|
|
}
|
|
|
|
// Utility functions
|
|
function showStep(step) {
|
|
document.querySelectorAll('.payment-step').forEach(div => div.classList.add('is-hidden'));
|
|
document.getElementById('step' + step).classList.remove('is-hidden');
|
|
}
|
|
|
|
function goBackToStep1() {
|
|
showStep(1);
|
|
selectedPaymentType = null;
|
|
}
|
|
|
|
function goBackToStep2() {
|
|
showStep(2);
|
|
}
|
|
|
|
function addAnotherPaymentMethod() {
|
|
// Reset form
|
|
document.getElementById('lookup_splynx_id').value = '';
|
|
currentCustomer = null;
|
|
selectedPaymentType = null;
|
|
setupIntentClientSecret = null;
|
|
showStep(1);
|
|
}
|
|
|
|
function showLoading(show) {
|
|
const loading = document.getElementById('loading');
|
|
const nextBtn = document.getElementById('nextBtn');
|
|
|
|
if (show) {
|
|
loading.classList.remove('is-hidden');
|
|
nextBtn.disabled = true;
|
|
} else {
|
|
loading.classList.add('is-hidden');
|
|
nextBtn.disabled = false;
|
|
}
|
|
}
|
|
|
|
function showError(message) {
|
|
document.getElementById('errorMessage').textContent = message;
|
|
document.getElementById('customerError').classList.remove('is-hidden');
|
|
}
|
|
|
|
function hideError() {
|
|
document.getElementById('customerError').classList.add('is-hidden');
|
|
}
|
|
|
|
// Allow Enter key to trigger next button in step 1
|
|
document.getElementById('lookup_splynx_id').addEventListener('keypress', function(e) {
|
|
if (e.key === 'Enter') {
|
|
fetchCustomerDetails();
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<style>
|
|
.payment-type-card {
|
|
cursor: pointer;
|
|
border: 2px solid transparent;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.payment-type-card:hover {
|
|
border-color: #dbdbdb;
|
|
}
|
|
|
|
.has-border-primary {
|
|
border-color: #3273dc !important;
|
|
}
|
|
|
|
#payment-element {
|
|
border: 1px solid #dbdbdb;
|
|
border-radius: 4px;
|
|
padding: 12px;
|
|
background-color: white;
|
|
min-height: 60px;
|
|
}
|
|
|
|
.stripe-element {
|
|
border: 1px solid #dbdbdb;
|
|
border-radius: 4px;
|
|
padding: 12px;
|
|
background-color: white;
|
|
}
|
|
|
|
.spinner {
|
|
width: 40px;
|
|
height: 40px;
|
|
border: 4px solid #f3f3f3;
|
|
border-top: 4px solid #3273dc;
|
|
border-radius: 50%;
|
|
animation: spin 1s linear infinite;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
@keyframes spin {
|
|
0% { transform: rotate(0deg); }
|
|
100% { transform: rotate(360deg); }
|
|
}
|
|
</style>
|
|
{% endblock %}
|