Add PromptPay support (#5)

* Handle PromptPay QR flow

* Send billing email for PromptPay

* fix isort

* Update payment.py

* Update signals.py

---------

Co-authored-by: Chondaen <chondaen12@1000WA>
This commit is contained in:
Daniel
2025-11-29 12:50:42 +07:00
committed by Raphael Michel
parent 449e8dc905
commit 3437b64947
6 changed files with 58 additions and 8 deletions

View File

@@ -137,7 +137,7 @@ logger = logging.getLogger('pretix.plugins.stripe')
# Real-time payments # Real-time payments
# - Swish: ✓ # - Swish: ✓
# - PayNow: ✗ # - PayNow: ✗
# - PromptPay: # - PromptPay:
# - Pix: ✗ # - Pix: ✗
# #
# Vouchers # Vouchers
@@ -428,6 +428,14 @@ class StripeSettingsHolder(BasePaymentProvider):
'before they work properly.'), 'before they work properly.'),
required=False, required=False,
)), )),
('method_promptpay',
forms.BooleanField(
label='PromptPay',
disabled=self.event.currency != 'THB',
help_text=_('Some payment methods might need to be enabled in the settings of your Stripe account '
'before they work properly.'),
required=False,
)),
('method_swish', ('method_swish',
forms.BooleanField( forms.BooleanField(
label=_('Swish'), label=_('Swish'),
@@ -1842,6 +1850,31 @@ class StripeSwish(StripeRedirectMethod):
} }
class StripePromptPay(StripeRedirectMethod):
identifier = 'stripe_promptpay'
verbose_name = _('PromptPay via Stripe')
public_name = 'PromptPay'
method = 'promptpay'
confirmation_method = 'automatic'
explanation = _(
'This payment method is available to PromptPay users in Thailand. Please have your app ready.'
)
def is_allowed(self, request: HttpRequest, total: Decimal=None) -> bool:
return super().is_allowed(request, total) and request.event.currency == "THB"
def _payment_intent_kwargs(self, request, payment):
return {
"payment_method_data": {
"type": "promptpay",
"billing_details": {
"email": payment.order.email,
},
},
}
class StripeTwint(StripeRedirectMethod): class StripeTwint(StripeRedirectMethod):
identifier = 'stripe_twint' identifier = 'stripe_twint'
verbose_name = _('TWINT via Stripe') verbose_name = _('TWINT via Stripe')

View File

@@ -47,15 +47,16 @@ def register_payment_provider(sender, **kwargs):
from .payment import ( from .payment import (
StripeAffirm, StripeAlipay, StripeBancontact, StripeCC, StripeEPS, StripeAffirm, StripeAlipay, StripeBancontact, StripeCC, StripeEPS,
StripeGiropay, StripeIdeal, StripeKlarna, StripeMobilePay, StripeGiropay, StripeIdeal, StripeKlarna, StripeMobilePay,
StripeMultibanco, StripePayPal, StripePrzelewy24, StripeRevolutPay, StripeMultibanco, StripePayPal, StripePromptPay, StripePrzelewy24,
StripeSEPADirectDebit, StripeSettingsHolder, StripeSofort, StripeSwish, StripeRevolutPay, StripeSEPADirectDebit, StripeSettingsHolder,
StripeTwint, StripeWeChatPay, StripeSofort, StripeSwish, StripeTwint, StripeWeChatPay,
) )
return [ return [
StripeSettingsHolder, StripeCC, StripeGiropay, StripeIdeal, StripeAlipay, StripeBancontact, StripeSettingsHolder, StripeCC, StripeGiropay, StripeIdeal, StripeAlipay, StripeBancontact,
StripeSofort, StripeEPS, StripeMultibanco, StripePrzelewy24, StripeRevolutPay, StripeWeChatPay, StripeSofort, StripeEPS, StripeMultibanco, StripePrzelewy24, StripePromptPay, StripeRevolutPay,
StripeSEPADirectDebit, StripeAffirm, StripeKlarna, StripePayPal, StripeSwish, StripeTwint, StripeMobilePay StripeWeChatPay, StripeSEPADirectDebit, StripeAffirm, StripeKlarna, StripePayPal, StripeSwish,
StripeTwint, StripeMobilePay
] ]

View File

@@ -325,6 +325,8 @@ $(function () {
} else if ($("#stripe_payment_intent_next_action_redirect_url").length) { } else if ($("#stripe_payment_intent_next_action_redirect_url").length) {
let payment_intent_next_action_redirect_url = $.trim($("#stripe_payment_intent_next_action_redirect_url").html()); let payment_intent_next_action_redirect_url = $.trim($("#stripe_payment_intent_next_action_redirect_url").html());
pretixstripe.handlePaymentRedirectAction(payment_intent_next_action_redirect_url); pretixstripe.handlePaymentRedirectAction(payment_intent_next_action_redirect_url);
} else if ($.trim($("#stripe_payment_intent_action_type").html()) === "promptpay_display_qr_code") {
waitingDialog.hide();
} else if ($.trim($("#stripe_payment_intent_action_type").html()) === "wechat_pay_display_qr_code") { } else if ($.trim($("#stripe_payment_intent_action_type").html()) === "wechat_pay_display_qr_code") {
let payment_intent_client_secret = $.trim($("#stripe_payment_intent_client_secret").html()); let payment_intent_client_secret = $.trim($("#stripe_payment_intent_client_secret").html());
pretixstripe.handleWechatAction(payment_intent_client_secret); pretixstripe.handleWechatAction(payment_intent_client_secret);

View File

@@ -58,6 +58,14 @@
<div class="text-center"> <div class="text-center">
<script type="text/plain" data-size="150" data-replace-with-qr>{{ payment_info.wechat.qr_code_url }}</script> <script type="text/plain" data-size="150" data-replace-with-qr>{{ payment_info.wechat.qr_code_url }}</script>
</div> </div>
{% elif payment.state == "created" and payment.provider == "stripe_promptpay" %}
<p>{% blocktrans trimmed %}
Please scan the QR code below to complete your PromptPay payment.
Once you have completed your payment, you can refresh this page.
{% endblocktrans %}</p>
<div class="text-center">
<img src="{{ payment_info.next_action.promptpay_display_qr_code.image_url_svg }}" alt="{% trans 'PromptPay QR code' %}" />
</div>
{% else %} {% else %}
<p>{% blocktrans trimmed %} <p>{% blocktrans trimmed %}
The payment transaction could not be completed for the following reason: The payment transaction could not be completed for the following reason:

View File

@@ -28,7 +28,11 @@
</div> </div>
<div class="panel-body embed-responsive embed-responsive-sca" id="scacontainer"> <div class="panel-body embed-responsive embed-responsive-sca" id="scacontainer">
{% if payment_intent_promptpay_image_url %}
<div class="text-center">
<img src="{{ payment_intent_promptpay_image_url }}" alt="{% trans 'PromptPay QR code' %}" />
</div>
{% endif %}
</div> </div>
</div> </div>
<div class="row checkout-button-row"> <div class="row checkout-button-row">

View File

@@ -595,7 +595,7 @@ class ScaView(StripeOrderView, View):
if intent.status == 'requires_action' and intent.next_action.type in [ if intent.status == 'requires_action' and intent.next_action.type in [
'use_stripe_sdk', 'redirect_to_url', 'alipay_handle_redirect', 'wechat_pay_display_qr_code', 'use_stripe_sdk', 'redirect_to_url', 'alipay_handle_redirect', 'wechat_pay_display_qr_code',
'swish_handle_redirect_or_display_qr_code', 'multibanco_display_details', 'swish_handle_redirect_or_display_qr_code', 'multibanco_display_details', 'promptpay_display_qr_code',
]: ]:
ctx = { ctx = {
'order': self.order, 'order': self.order,
@@ -613,6 +613,8 @@ class ScaView(StripeOrderView, View):
elif intent.next_action.type == 'multibanco_display_details': elif intent.next_action.type == 'multibanco_display_details':
ctx['payment_intent_next_action_redirect_url'] = intent.next_action.multibanco_display_details['hosted_voucher_url'] ctx['payment_intent_next_action_redirect_url'] = intent.next_action.multibanco_display_details['hosted_voucher_url']
ctx['payment_intent_redirect_action_handling'] = 'iframe' ctx['payment_intent_redirect_action_handling'] = 'iframe'
elif intent.next_action.type == 'promptpay_display_qr_code':
ctx['payment_intent_promptpay_image_url'] = intent.next_action.promptpay_display_qr_code['image_url_svg']
r = render(request, 'pretixplugins/stripe/sca.html', ctx) r = render(request, 'pretixplugins/stripe/sca.html', ctx)
r._csp_ignore = True r._csp_ignore = True