Implementing Stripe Payments in Docly: A Step-by-Step Guide

Today, we're excited to walk you through the process of integrating Stripe, a leading online payment platform, into your Docly applications. An effective and seamless payment process can drastically enhance your user experience, and we're here to show you just how straightforward this implementation can be.

Before you start, please ensure you have registered with Stripe and obtained your unique API keys. Remember, Stripe provides separate keys for testing and live environments, so make sure you use the correct key for your current development stage.

Now, let's dive into the steps needed to incorporate Stripe payments in your Docly application:

  • Installing the Stripe JavaScript library:
  • Begin by adding the Stripe.js library to your project. This is the base layer that will allow your application to interact with the Stripe API. It's as easy as including the Stripe.js script in your HTML.
  • Creating a payment form:
  • With the Stripe library in place, you'll need to create a payment form. This form will gather your users' card information. Remember, to maintain PCI compliance, it's important that this data is sent directly to Stripe's servers and never touches your own.
  • Creating a Stripe token:
  • When a user submits the payment form, you will need to convert the sensitive card data into a Stripe token. This is accomplished using a function provided by the Stripe.js library.
  • Submitting the payment:
  • With the token now generated, you can submit it to your server, where it will be used to make a charge via the Stripe API.
  • Handling the response:
  • Finally, it's essential to handle the response from the Stripe API. Depending on the result of the transaction, you may need to update your application's UI or database.

Creating a config file

Create a schema for your application with your desired settings. We always recommend storing this under the # folder, and a great name for this config file is simply "Config". And a great best practice for your config file schema is "<Application name> config". Enter you application name in the brackets.

Example:

From your shopping cart - call API to create an order

function submitOrder() {
    scroll("#contents");
    $("#checkout-element").hide();
    $("#loader").show();

    var order = { items : cart.items, name : name, email:email, phoneno:phoneno, terms:terms, validation:validation, total: cart.total };
    var data = { order : JSON.stringify(order) };
    console.log(order);
    $.ajax({
        type:"POST",
        url: "#request.sitepath#API/SubmitOrder", 
        data: data, 
        dataType: "json",
        error: function (request, error) {
            console.log(request, error);
            var err = JSON.parse(request.responseText);
            alert(err.Message);
            
            $("#checkout-element").show();
            $("#loader").hide();
        },
        success: function (result) {
            console.log("Submit response", result);
            window.localStorage.setItem("cart_payment", JSON.stringify(result));
            setupPayment(result);
        }
    });
}

Create API function to create the order file and Stripe Order (SubmitOrder.js)

In this function we are also checking a google recaptcha validation, which is something you must do to prevent hacking.

// Leser ordren fra parameter sendt inn fra JS
if (!form.order) Assert("Posted form is missing in request!");

let order = JSON.parse(form.order);

// Validate form 
if (!docly.isName(order.name)) docly.assert("Name is required!");
if (!docly.isEmail(order.email)) docly.assert("Email is required!");
if (!docly.isString(order.phoneno, 8)) docly.assert("Phoneno is required!");
if (!docly.isString(order.validation)) docly.assert("Google recaptcha validation token is missing!");

if(!order.items)
    Assert("Shopping cart is empty!");


// Check Google token
// Iht. guide https://developers.google.com/recaptcha/docs/verify
let config = docly.getFile("#/Config");

let googleUrl = "https://www.google.com/recaptcha/api/siteverify?response=" + order.validation + "&secret=" + config.GoogleRE;
let result = docly.httpPost(googleUrl, {});
if (!result.success) docly.assert("Google validation failed!");


// Calculate value of items
let total = 0.0;
let items = [];
for(var line of order.items) {
    let product = docly.getFile(docly.urlDecode(line.Url));
    
    if (product == null)
        Assert("An invalid / outdated product has been specified " + line.url);

    total += parseFloat(product.Price);

    let item =  {
        "Product" : product.filename,
        "Url" : product.Url,
        "Variant" : line.Variant,
        "Length" : line.Length,
        "Price" : product.Price
    };

    items.push(item);
}

if (total == 0) {
    Assert("No items / invalid items in shopping cart (order totaled to 0,-)!");
}

if (parseFloat(total) != parseFloat(order.total)) {
    Assert("Outdated prices in shopping cart, was "+ total+ " expected "+ order.total + " | items count: " + order.items.Count);
}

// Save order to Docly server
let save = {
  "Name" : order.name,
  "Email" : order.email,
  "Phoneno" : order.phoneno,
  "Items" : items,
  "Total" : total,
  "IP" : getRemoteIP(),
  "Google_response" : JSON(result)
};


let timestamp = docly.format(getdate(), "yyyyMMdd-HHmmss");
let filename = timestamp + " - " + order.email;
let filepath = "#/Orders/" + filename;
if (FileExists(filepath))
    Assert("Failed to create order, please try again!");

let saved = docly.saveFile(filepath, save, "#/My shop order");

let stripeamount = parseInt(total * 100);

// Create stripe order (payment intent)
let stripe = {
    "amount" : stripeamount,
    "currency" : config.Currency,
    "description" : "Shop order",
    "receipt_email" : order.email
};

let headers = { "Authorization" :  "Bearer " + config.Key };
let payment = docly.httpFormPost("https://api.stripe.com/v1/payment_intents", stripe, headers);

// Update order on server
save["StripeId"] = payment.id;
save["Stripe_response"] = JSON.stringify(payment);
let update = docly.saveFile(filepath, save, "#/My shop order");

// Return payment info / url
let final =  {
    "stripeSecret" : payment.client_secret,
    "orderid" : filename,
    "timestamp" : getdate()
};

return final;

Initiating payment element in your HTML page

<script src="https://js.stripe.com/v3/"></script>
<script>
    #{
      var config = docly.getFile("#/Config");
      var publishableKey = config.ID;
    }#
    
    const stripe = Stripe("#publishableKey#", {
        apiVersion: '2020-08-27',
    });
    
    function submitOrder() {
        scroll("#contents");
        $("#checkout-element").hide();
        $("#loader").show();
    
        var order = { items : cart.items, name : name, email:email, phoneno:phoneno, terms:terms, validation:validation, total: cart.total };
        var data = { order : JSON.stringify(order) };
        console.log(order);
        $.ajax({
            type:"POST",
            url: "#request.sitepath#API/SubmitOrder", 
            data: data, 
            dataType: "json",
            error: function (request, error) {
                console.log(request, error);
                var err = JSON.parse(request.responseText);
                alert(err.Message);
                
                $("#checkout-element").show();
                $("#loader").hide();
            },
            success: function (result) {
                console.log("Submit response", result);
                window.localStorage.setItem("cart_payment", JSON.stringify(result));
                setupPayment(result);
            }
        });
    }
</script>

Create a return page

After a completed payment the user will be returned to your page:

<!--#master file="#/master.hash"-->
<html lang="en">
  <head>
    <title xdt:Transform="Replace">Checkout page</title>
  </head>
  <body>
      
      <section id="contents" xdt:Transform="Replace" style="min-height:200px">

      
        <section id="loader">
          <div class="loader">
              <div class="duo duo1">
                <div class="dot dot-a"></div>
                <div class="dot dot-b"></div>
              </div>
              <div class="duo duo2">
                <div class="dot dot-a"></div>
                <div class="dot dot-b"></div>
              </div>
            </div>
        </section>

        <div class="container" id="success" style="display:none">
            <br>
            <h1 class="hero-heading mb-1 mb-sm-3 mt-sm-2">
                <i class="fas fa-check-circle text-success"></i> Thanks for your order</h1>
                <p class="lead text-muted mb-6">Your receipt and more information about pickup has been sent to your email.</p>
    
        </div>
          
        <div class="container" id="failed" style="display:none">
            <br>
            <h1 class="hero-heading mb-1 mb-sm-3 mt-sm-2">
                <i class="fas fa-times-circle text-danger"></i> Payment failed!</h1>
                <p class="lead text-muted mb-6">Your payment was not registered successfully, please try again or contact support if you need assistanse.</p>
        </div>
    </section>


    <div id="ActivePaymentDiv" xdt:Transform="Replace"></div>

<section id="pagescript" xdt:Transform="Replace">
    <script>

        function scroll(target) {
            $("html, body").animate({
                    scrollTop: $(target).offset().top - 160
                }, 200);
        }
        
        function getParam(name) {
            const urlParams = new URLSearchParams(window.location.search);
            return myParam = urlParams.get(name);
        }
        

        // Submits order to server
        function retreivePaymentStatus() {
            var data = { id : getParam("id") };
            console.log(data);
            $.get({
                url: "#request.sitepath#%23/API/CheckPayment", 
                data: data, 
                dataType: "json",
                error: function (request, error) {
                    console.log(request, error);
                    var err = JSON.parse(request.responseText);
                    alert(err.Message);
                },
                success: function (result) {
                    console.log("Submit response", result);

                    if (result.Success) {
                        $("#loader").hide();
                        $("#success").show();
                        cartEmpty();
                    } else {
                        $("#loader").hide();
                        $("#failed").show();
                    }
                    
                }
            });
        }
        
        $(function() {
           retreivePaymentStatus(); 
        });


    </script>
    
</section>
    
  
  </body>
</html>

Create API function to update payment (CheckPayment.js)

let orderid = query.id;

if (!orderid) Assert("ID parameter not specified!");

// Sjekk oppdatert status fra Stripe
let config = docly.getFile("#/Config");
let filename = "#/Orders/" + orderid;
let order = docly.getFile(filename);

if (order == null) Assert("Order not found with specified ID!");

// Hent  status på ordren:
let headers = { "Authorization" :  "Bearer " + config.Key };
let payment = HttpGet("https://api.stripe.com/v1/payment_intents/" + order.StripeId, headers);

//return JSON.stringify(payment);

// Oppdater status på ordren hvis endret:
if (order.StripeStatus != payment.status)
{
    order["StripeStatus"] = payment.status;
    SaveFile(filename, order);
    
    if (payment.status == "succeeded") {
        // Send receipt
        docly.sendEmail(filename, "Receipt.html.pdf", order.Email, "Order confirmation", config.OrderMessage);
        
        // Send notification
        docly.sendEmail(filename, "Order.html.pdf", config.Notifications, "New order placed", "Someone placed an order. Order is attached. Please verify that payment has been successfully charged in Stripe.");
    }
}

let data = {
    Success : (payment.status == "succeeded")
};

return data;

Happy Coding!

Get in touch with support if you need any help with this!