|
|
|
@ -31,7 +31,11 @@ |
|
|
|
<div class="level"> |
|
|
|
<div class="level-left"> |
|
|
|
<div class="level-item"> |
|
|
|
{% if payment.Success == True %} |
|
|
|
{% if payment.Refund == True %} |
|
|
|
<span class="icon is-large" style="color: #9370db;"> |
|
|
|
<i class="fas fa-undo fa-2x"></i> |
|
|
|
</span> |
|
|
|
{% elif payment.Success == True %} |
|
|
|
<span class="icon is-large has-text-success"> |
|
|
|
<i class="fas fa-check-circle fa-2x"></i> |
|
|
|
</span> |
|
|
|
@ -47,7 +51,13 @@ |
|
|
|
</div> |
|
|
|
<div class="level-item"> |
|
|
|
<div> |
|
|
|
{% if payment.Success == True %} |
|
|
|
{% if payment.Refund == True %} |
|
|
|
<h2 class="title is-4 mb-2" style="color: #9370db;">Payment Refunded</h2> |
|
|
|
<p class="has-text-grey">This payment has been refunded to the customer.</p> |
|
|
|
{% if payment.Stripe_Refund_Created %} |
|
|
|
<p class="has-text-grey is-size-7">Refunded: {{ payment.Stripe_Refund_Created.strftime('%Y-%m-%d %H:%M:%S') }}</p> |
|
|
|
{% endif %} |
|
|
|
{% elif payment.Success == True %} |
|
|
|
<h2 class="title is-4 has-text-success mb-2">Payment Successful</h2> |
|
|
|
<p class="has-text-grey">This payment has been completed successfully.</p> |
|
|
|
{% elif payment.Success == False %} |
|
|
|
@ -61,12 +71,24 @@ |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div class="level-right"> |
|
|
|
{% if payment.PI_FollowUp %} |
|
|
|
<button class="button is-warning" id="checkIntentBtn" onclick="checkPaymentIntent()"> |
|
|
|
<span class="icon"><i class="fas fa-sync-alt"></i></span> |
|
|
|
<span>Force Check Status</span> |
|
|
|
</button> |
|
|
|
{% endif %} |
|
|
|
<div class="field is-grouped"> |
|
|
|
{% if payment.Refund != True and payment.Success == True %} |
|
|
|
<div class="control"> |
|
|
|
<button class="button" style="border-color: #9370db; color: #9370db;" id="refundBtn" onclick="showRefundModal()"> |
|
|
|
<span class="icon"><i class="fas fa-undo"></i></span> |
|
|
|
<span>Process Refund</span> |
|
|
|
</button> |
|
|
|
</div> |
|
|
|
{% endif %} |
|
|
|
{% if payment.PI_FollowUp %} |
|
|
|
<div class="control"> |
|
|
|
<button class="button is-warning" id="checkIntentBtn" onclick="checkPaymentIntent()"> |
|
|
|
<span class="icon"><i class="fas fa-sync-alt"></i></span> |
|
|
|
<span>Force Check Status</span> |
|
|
|
</button> |
|
|
|
</div> |
|
|
|
{% endif %} |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
@ -165,21 +187,58 @@ |
|
|
|
{% endif %} |
|
|
|
</div> |
|
|
|
{% endif %} |
|
|
|
|
|
|
|
{% if payment.Refund == True %} |
|
|
|
<div class="notification is-light" style="background-color: #f8f4ff; border-color: #9370db;"> |
|
|
|
<span class="icon" style="color: #9370db;"><i class="fas fa-undo"></i></span> |
|
|
|
<strong style="color: #9370db;">Refund Processed:</strong> This payment has been refunded. |
|
|
|
{% if payment.Stripe_Refund_Created %} |
|
|
|
<br><small>Refunded: {{ payment.Stripe_Refund_Created.strftime('%Y-%m-%d %H:%M:%S') }}</small> |
|
|
|
{% endif %} |
|
|
|
{% if payment.Stripe_Refund_ID %} |
|
|
|
<br><small>Refund ID: <code>{{ payment.Stripe_Refund_ID }}</code></small> |
|
|
|
{% endif %} |
|
|
|
</div> |
|
|
|
{% endif %} |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- Error Information --> |
|
|
|
{% if payment.Error %} |
|
|
|
{% set error_alert = payment | error_alert %} |
|
|
|
<div class="box"> |
|
|
|
<h3 class="title is-5 has-text-danger"> |
|
|
|
<span class="icon"><i class="fas fa-exclamation-triangle"></i></span> |
|
|
|
Error Information |
|
|
|
Payment Error Details |
|
|
|
</h3> |
|
|
|
|
|
|
|
{% if error_alert %} |
|
|
|
<div class="error-alert {{ error_alert.type }}"> |
|
|
|
<div class="error-alert-header"> |
|
|
|
<span class="icon"><i class="fas {{ error_alert.icon }}"></i></span> |
|
|
|
<span class="error-title">{{ error_alert.title }}</span> |
|
|
|
</div> |
|
|
|
<div class="error-alert-body"> |
|
|
|
<p class="error-message">{{ error_alert.message }}</p> |
|
|
|
<p class="error-suggestion"><strong>Suggested Action:</strong> {{ error_alert.suggestion }}</p> |
|
|
|
<details class="error-details"> |
|
|
|
<summary>View Technical Details</summary> |
|
|
|
<pre>{{ error_alert.raw_error }}</pre> |
|
|
|
</details> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
{% else %} |
|
|
|
<!-- Fallback for unclassified errors --> |
|
|
|
<div class="notification is-danger is-light"> |
|
|
|
<pre>{{ payment.Error }}</pre> |
|
|
|
<h5 class="title is-6">Payment Error</h5> |
|
|
|
<p>An error occurred during payment processing.</p> |
|
|
|
<details class="mt-3"> |
|
|
|
<summary class="has-text-grey">Technical Details</summary> |
|
|
|
<pre class="mt-2">{{ payment.Error }}</pre> |
|
|
|
</details> |
|
|
|
</div> |
|
|
|
{% endif %} |
|
|
|
</div> |
|
|
|
{% endif %} |
|
|
|
|
|
|
|
@ -230,6 +289,76 @@ |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
{% endif %} |
|
|
|
|
|
|
|
{% if payment.Refund_JSON %} |
|
|
|
<div class="column"> |
|
|
|
<div class="box"> |
|
|
|
<h3 class="title is-5"> |
|
|
|
<span class="icon" style="color: #9370db;"><i class="fas fa-undo"></i></span> |
|
|
|
Refund JSON |
|
|
|
</h3> |
|
|
|
|
|
|
|
<div class="field is-grouped"> |
|
|
|
<div class="control"> |
|
|
|
<button class="button is-small" style="border-color: #9370db; color: #9370db;" onclick="copyFormattedJSON('refund-json-content')"> |
|
|
|
<span class="icon"><i class="fas fa-copy"></i></span> |
|
|
|
<span>Copy JSON</span> |
|
|
|
</button> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<pre class="is-size-7"><code>{{ payment.Refund_JSON | format_json }}</code></pre> |
|
|
|
<div id="refund-json-content" style="display: none;">{{ payment.Refund_JSON | format_json }}</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
{% endif %} |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- Refund Modal --> |
|
|
|
<div class="modal" id="refundModal"> |
|
|
|
<div class="modal-background"></div> |
|
|
|
<div class="modal-card"> |
|
|
|
<header class="modal-card-head" style="background-color: #9370db;"> |
|
|
|
<p class="modal-card-title has-text-white"> |
|
|
|
<span class="icon"><i class="fas fa-undo"></i></span> |
|
|
|
Process Refund |
|
|
|
</p> |
|
|
|
<button class="delete" aria-label="close" onclick="hideModal('refundModal')"></button> |
|
|
|
</header> |
|
|
|
<section class="modal-card-body"> |
|
|
|
<div class="has-text-centered py-4"> |
|
|
|
<span class="icon is-large mb-4" style="color: #9370db;"> |
|
|
|
<i class="fas fa-undo fa-3x"></i> |
|
|
|
</span> |
|
|
|
<div class="content"> |
|
|
|
<h4 class="title is-5">Confirm Refund</h4> |
|
|
|
<p>Are you sure you want to process a refund for this payment?</p> |
|
|
|
<p><strong>Payment Amount:</strong> {{ payment.Payment_Amount | currency }}</p> |
|
|
|
<p><strong>Customer:</strong> {{ payment.Splynx_ID }}</p> |
|
|
|
|
|
|
|
<div class="field"> |
|
|
|
<label class="label">Refund Reason</label> |
|
|
|
<div class="control"> |
|
|
|
<div class="select is-fullwidth"> |
|
|
|
<select id="refundReason"> |
|
|
|
<option value="requested_by_customer">Requested by Customer</option> |
|
|
|
<option value="duplicate">Duplicate Payment</option> |
|
|
|
<option value="fraudulent">Fraudulent</option> |
|
|
|
</select> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</section> |
|
|
|
<footer class="modal-card-foot is-justify-content-center"> |
|
|
|
<button class="button" style="border-color: #9370db; color: #9370db;" onclick="processRefund()" id="processRefundBtn"> |
|
|
|
<span class="icon"><i class="fas fa-undo"></i></span> |
|
|
|
<span>Process Refund</span> |
|
|
|
</button> |
|
|
|
<button class="button" onclick="hideModal('refundModal')">Cancel</button> |
|
|
|
</footer> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- Success Modal --> |
|
|
|
@ -292,6 +421,55 @@ |
|
|
|
</div> |
|
|
|
|
|
|
|
<script> |
|
|
|
function showRefundModal() { |
|
|
|
document.getElementById('refundModal').classList.add('is-active'); |
|
|
|
} |
|
|
|
|
|
|
|
function processRefund() { |
|
|
|
const btn = document.getElementById('processRefundBtn'); |
|
|
|
const originalText = btn.innerHTML; |
|
|
|
const reason = document.getElementById('refundReason').value; |
|
|
|
|
|
|
|
// Disable button and show loading |
|
|
|
btn.disabled = true; |
|
|
|
btn.innerHTML = '<span class="icon"><i class="fas fa-spinner fa-spin"></i></span><span>Processing...</span>'; |
|
|
|
|
|
|
|
// Make API call to process refund |
|
|
|
fetch(`/single-payment/refund/{{ payment.id }}`, { |
|
|
|
method: 'POST', |
|
|
|
headers: { |
|
|
|
'Content-Type': 'application/json', |
|
|
|
}, |
|
|
|
body: JSON.stringify({ |
|
|
|
reason: reason |
|
|
|
}) |
|
|
|
}) |
|
|
|
.then(response => response.json()) |
|
|
|
.then(data => { |
|
|
|
hideModal('refundModal'); |
|
|
|
if (data.success) { |
|
|
|
showSuccessModal(` |
|
|
|
<h4 class="title is-5">Refund Processed Successfully!</h4> |
|
|
|
<p>The refund has been processed and sent to Stripe.</p> |
|
|
|
<p><strong>Refund ID:</strong> ${data.refund_id}</p> |
|
|
|
<p><strong>Amount:</strong> ${data.amount_refunded}</p> |
|
|
|
`); |
|
|
|
} else { |
|
|
|
showErrorModal(data.error || 'Failed to process refund'); |
|
|
|
} |
|
|
|
}) |
|
|
|
.catch(error => { |
|
|
|
hideModal('refundModal'); |
|
|
|
console.error('Error processing refund:', error); |
|
|
|
showErrorModal('Failed to process refund. Please try again.'); |
|
|
|
}) |
|
|
|
.finally(() => { |
|
|
|
// Re-enable button |
|
|
|
btn.disabled = false; |
|
|
|
btn.innerHTML = originalText; |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
function checkPaymentIntent() { |
|
|
|
const btn = document.getElementById('checkIntentBtn'); |
|
|
|
const originalText = btn.innerHTML; |
|
|
|
|