Payments
SocialStack has an installable module for handling payments, shopping carts, products and subscriptions amongst other payment related functionality.
Getting Started[edit | edit source]
First, install the payments module:
socialstack install payments
This will pull in multiple separate modules, e.g. Api/Payments. When you next start the project, you'll then have a products and prices area of your admin panel.
Configuring the module[edit | edit source]
Currency codes[edit | edit source]
Prices on products can be localised and are localised by various components on the frontend such as the default UI/Payments/Checkout component. For this to work, it's important that you go into the Locales part of the admin panel and set the "Currency code" on your locale(s). This currency code is, for example, "USD" or "GBP", depending on what your primary audience is. You can also add additional locales to your site and set currency codes on each one.
The currency code field on locales is used by the frontend to pick how to display prices
Payment gateway[edit | edit source]
Next, you'll need to setup a payment gateway. By default Stripe is ready to go, so you'll need a test Stripe publishable/ private key. Once you have this info, head into the Configuration part of the administration panel where you should see a Stripe entry, and drop in your details. At a minimum, only PublishableKey and SecretKey are required. If the Stripe entry is not there, you can create it too, or restart the API if you haven't done so yet since installing the module. Config entries are created automatically if they don't exist during startup.
Once you have setup the payment gateway, it's important that you add a webhook to your site. On the Stripe dashboard, create a webhook to https://yourSiteOrStageSite.com/v1/stripe-gateway/webhook.
This of course won't work when testing locally because Stripe can't access your computer on a LAN (aside from using a Stripe webhook simulator) so the webhook is best set to point at a stage site. You can test your webhook setup is working by using one of Stripe's test payment cards with delayed authorisation such as 3DS. The purchase request is created at Stripe and is marked as pending in your API. Stripe provides the 3DS URL to the frontend which you must then display - the default checkout component (UI/Payments/Checkout) will simply redirect to it. When the user successfully completes the 3DS challenge, the webhook is pinged by Stripe to indicate the payment completed successfully.
Adding products[edit | edit source]
In short, a product can have multiple tiers (for bulk pricing), and a product's price can be localised (for international support). The UI for this will be specialised as a pricing grid in the future, but for now you'll need to create separate prices and separate products per tier, if your product has both bulk prices and is localised.
When adding prices, the Amount is in the smallest unit of a currency. In US Dollars and Euro's for example, that is in cents. In Pound Sterling, it is in pence.
A product also has pricing strategies - see the separate page for more information on how those work.
Using built in UI's[edit | edit source]
- Typically sites use the shopping cart checkout flow. This uses a checkout page which is suggested to use /checkout as the URL and contain UI/Payments/Checkout on it. You must also add a page with default URL set to "/complete" with UI/Payments/Complete on it.
- Display what is currently in the cart with the UI/Payments/Cart component.
- You can add to the cart via the useCart hook:
import { useCart } from 'UI/Payments/CartSession';
...
var { addToCart } = useCart();
...
addToCart({product: 14, quantity: 20}); // Add 20 of product #14 to the cart.
addToCart({product: 19, isSubscribing: true}); // Adds the subscription product #19 to the cart.
addToCart({product: 14, quantity: 1, ref: 'test'}); // Add 1 of product #14 to the cart with a reference of test.
- Or alternatively to add to the cart, the UI/Payments/ProductQuantity component. This has a +/- quantity UI and an add to cart button:
import { useCart } from 'UI/Payments/CartSession';
...
var { getCartQuantity } = useCart();
...
<ProductQuantity product={productObject} quantity={getCartQuantity(productObject.id)}/>
- If you would just like a payment method input/selector, which includes selecting previously saved payment methods, use type='payment'. Note that the saved payment methods are saved as a token - your site will only store the non-sensitive details such as the last 4 digits and the expiry date. Because of this the full requirements of PCI DSS are avoidable as by default the payment card details never touch your site's servers.
<Input type='payment' name='paymentMethod' label='Payment method' validate={['Required']} />
Working inside the API[edit | edit source]
In a nutshell, the payments module will ultimately create a Purchase with ProductQuantity entries attached to it, then attempt to execute the purchase. For example if a Subscription is created, each time the subscription is billed it will raise a new Purchase and copy over the Subscriptions ProductQuantity entries. This allows historical purchases to list out the items even if the current subscription has been modified in some way.