Hi,
I am currently trying to implement stripe as an inline payment option for our client, we are working on DynamicWeb 10.4.1.
I have followed the instructions given here: https://doc.dynamicweb.com/forum/dynamicweb-10/dynamicweb-10/post-error-template-from-payment-provider
to allow me to select the Post and Error templates and have their counterparts created in the Design section of the Assets View.
I have configured stripe to use Test Mode and also set it to inline mode.
I have added the tag to my PaymentUser.cshtml template like this:
<div class="grid gap-0 h-100">
<div class="cart g-col-12 g-col-lg-8 p-3 p-lg-4 pe-xl-5 ps-xl-6 ps-xxl-8 order-last order-lg-first@(theme)">
<form name="ordersubmit" id="ordersubmit" method="post" autocomplete="off" style="max-width: 65rem; margin-left: auto;">
@Include("Helpers/Logo.cshtml")
@Include("Helpers/StepsBreadcrumbs.cshtml")
@Include("Helpers/Errors.cshtml")
@Include("Shared/Helpers/StepSummary_v2.cshtml")
<div class="mt-4">
<h3 class="fs-6 fw-normal mb-0">@Translate("Select payment")</h3>
<p class="fs-8 mb-3">@Translate("All transactions are encrypted")</p>
<!-- Render the Stripe Post Template -->
@GetString("Ecom:Cart.PaymentInlineForm")
</div>
@Include("Helpers/OrderReference.cshtml")
@Include("Helpers/OrderComment.cshtml")
@Include("Helpers/StepsNavigation.cshtml")
<input type="hidden" id="CurrentStep">
</form>
</div>
@Include("Helpers/SummarySidebar.cshtml")
</div>
<script>
function submitForm() {
document.querySelector("#CurrentStep").name = "@GetString("CartV2.CurrentStepButtonName")";
swift.PageUpdater.Update(document.querySelector("#ordersubmit"));
}
</script>
This correctly renders the card element input when Stripe is the chosen payment method.
The next step here is understanding what should be in the post template that is being rendered. The documentation around this for both DW9 and DW10 mentions nothing more than that they need to be selected, but gives no help on how to wire up the templates or what should be inside them. This would be a huge help for myself and the next person that attempts this.
I have attempted to implement the post template from the information i have gained from my previous knowledge of implementing Stripe manually on different websites and what the StripeCheckoutHandler code is doing along with the documentation from Stripe, but i am still missing what i hope, is the final piece of the puzzle.
When i have implemented Stripe in the past for other clients, you would ordinarily create a 'PaymentElement' object (see: https://docs.stripe.com/payments/quickstart), and then do a call to the server to get a ClientSecret from the Stripe .NET Library by creating a paymentIntent. From looking at the checkout handler, this does not appear to be supported, and instead you are stuck using the older 'Card' element, which instead, will generate a token on form submission that you then post back to the CheckoutHandler as a hidden form field, this in turn will then create the PaymentIntent internally and handle it all server-side. The other issue with this, is that Stripe no longer advocate using it, so they do not provide documenation on setting this up anymore. This is the only reference they provide to the card element that i can find: https://docs.stripe.com/js/element/other_element
I have attempted to set this up anyway, but i am at a loss on how to correctly post this back to the checkout handler as i currently just get redirected back to the start of the checkout flow on form submission after filling out the card details.
Here is my current Post Template, which uses a Stripe Card element:
@inherits Dynamicweb.Rendering.RazorTemplateBase<Dynamicweb.Rendering.RazorTemplateModel<Dynamicweb.Rendering.Template>>
<script src="https://js.stripe.com/v3/"></script>
<h2>Card</h2>
<div id="card-element">
<!-- Stripe card element will be inserted here. -->
</div>
<script type="text/javascript">
const stripe = Stripe('@GetString("Stripe.publishablekey")');
const options = {
hidePostalCode: true,
disableLink: true
};
let elements = stripe.elements();
// Setting up Stripe's Card Element
let card = elements.create("card", options);
card.mount('#card-element');
let form = document.getElementById('ordersubmit');
form.addEventListener('submit', function(event) {
event.preventDefault();
// Creates a token
stripe.createToken(card).then(function(result) {
if (result.error) {
// Inform the user if there was an error
let errorElement = document.getElementById('card-errors');
errorElement.textContent = result.error.message;
} else {
// Send the created token to your server
stripeTokenHandler(result.token);
}
});
});
function stripeTokenHandler(token) {
// Insert the token ID into the form so it gets submitted to the server
let form = document.getElementById('ordersubmit');
let hiddenInput = document.createElement('input');
hiddenInput.setAttribute('type', 'hidden');
hiddenInput.setAttribute('name', 'stripeToken');
hiddenInput.setAttribute('value', token.id);
form.appendChild(hiddenInput);
// A Guess from looking at the StripeCheckoutHandler code that it expects a hidden element with the name 'action' and a value of 'Approve' to be sent in the form submission
let actionInput = document.createElement('input');
actionInput.setAttribute('type', 'hidden');
actionInput.setAttribute('name', 'action');
actionInput.setAttribute('value', 'Approve');
form.appendChild(actionInput);
// A Guess from looking at the StripeCheckoutHandler code that it expects a hidden element with the name 'OrderIdRequestName' and a value of <Current Order Id> to be sent in the form submission
let orderIdInput = document.createElement('input');
orderIdInput.setAttribute('type', 'hidden');
orderIdInput.setAttribute('name', 'OrderIdRequestName');
orderIdInput.setAttribute('value', '@GetString("Ecom:Order.ID")');
form.appendChild(orderIdInput);
// Submit the form
form.submit();
}
</script>
My Understanding, from looking at the StripeCheckoutHandler code, is that i want to hit a method to process the token and do the work server-side,
which i then expect will return back to the front end with a status of Authorised, Declined or Requires_attention etc.
In the event i get an Authorised result i want to convert the cart to an order and redirect to the Receipt Step
In the event i get a Declined result i want to render the Error Template with the stripe error message
In the event i get a Requires_Attention result i want to initiate the 3DS2 Challenge flow to enable them to confirm payment on their mobile banking app.
This is the method in the checkoutHandler, that i think i need to hit:
/// <summary>
/// <para>Function that is called from CartV2 when OrderIdRequestName is in the Request.</para>
/// <para>Implement this method to receive redirects from CartV2</para>
/// </summary>
/// <param name="order">The order with order ID equal to the request OrderIdRequestName</param>
/// <returns>Returns the output to the module.</returns>
/// <remarks>
/// <para>The most common use of this method is to receive callbacks and redirect urls from a gateway.</para>
/// <para>
/// To enable the Redirect method to be called on a callback parse an url with the current page id and the order id
/// in the query string to the gateway as the
/// callback url. The order id must be stored in a query string variable named with the return value of the
/// property OrderIdRequestName of the CheckoutHandler base
/// class.
/// </para>
/// <para></para>
/// <para>
/// Return a <see cref="T:Dynamicweb.Frontend.OutputResult" /> to control the flow of checkout, be that content or redirection.
/// </para>
/// </remarks>
public virtual OutputResult HandleRequest(Order order)
{
//...
}
Would anyone be able to give me some guidance on what it is that i am missing to enable me to accept a payment using Stripe, as i have spent quite alot of time
already on this and have no more ideas on what to do next.
Also, once i get this working, i have a requirement to also implement Google/Apple Pay and Paypal through stripe as seperate payment methods
(other instances of the StripeCheckoutHandler with different Post Templates is my current thinking),
However, Wallets and Paypal do not appear to be supported with a Card element according to: https://docs.stripe.com/payments/payment-card-element-comparison
Can someone confirm if this is the case, or if i need to use a different element such as the 'paymentRequestButton'?
Finally, as this site is in the UK, it will be mandatory to enable 3ds2 security on any transactions, which i am also not so certain is possible with
the current code in the StripeCheckoutHandler. This would be good to know now, so if i have to write my own handler i can start than sooner rather than later, although its really not
and ideal scenario.
Any help would really be appeciated!
Kind Regards,
Joe