
Buy Now Pay Later
- 60 installs
- 41 repo stars
- Updated March 13, 2026
- finsilabs/awesome-ecommerce-skills
Offer Klarna, Afterpay, or Affirm installment payments at checkout using native platform integrations to reduce hesitation and raise average order value.
About
A skill for adding buy-now-pay-later installment options (Klarna, Afterpay, Affirm) to ecommerce checkout via native integrations. A developer uses it to offer financing at checkout without custom code.
- Native Klarna/Afterpay/Affirm integrations per platform
- Targets higher AOV and lower abandonment on high-ticket items
Buy Now Pay Later by the numbers
- 60 all-time installs (skills.sh)
- Ranked #3,161 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/finsilabs/awesome-ecommerce-skills --skill buy-now-pay-laterAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 60 |
|---|---|
| repo stars | ★ 41 |
| Last updated | March 13, 2026 |
| Repository | finsilabs/awesome-ecommerce-skills ↗ |
What it does
Offer Klarna, Afterpay, or Affirm installment payments at checkout using native platform integrations to reduce hesitation and raise average order value.
Files
Buy Now, Pay Later (BNPL)
Overview
Buy Now, Pay Later lets shoppers split purchases into 4 interest-free installments or longer financing plans. Offering BNPL at checkout typically increases average order value (AOV) by 15–40% and reduces checkout abandonment for high-ticket items. The three major providers — Klarna, Afterpay (Clearpay in UK), and Affirm — each have native integrations for Shopify, WooCommerce, and BigCommerce that require no custom code to install.
When to Use This Skill
- When AOV is in the $100–$3,000 range where installments meaningfully help conversion
- When analytics show customers abandoning checkout at the payment step due to price
- When competitors offer BNPL and it is becoming a category expectation (fashion, electronics, furniture)
- When adding BNPL to an existing Stripe integration (Klarna and Afterpay are available as Stripe payment methods)
Core Instructions
Step 1: Determine your platform and choose the right BNPL approach
| Platform | Recommended Approach | Notes |
|---|---|---|
| Shopify | Enable via Shopify Payments (Klarna, Afterpay built in) or install provider's app | Shopify Payments includes Klarna and Afterpay with zero additional integration; just enable them |
| WooCommerce | Install the provider's official WooCommerce plugin | Klarna, Afterpay, and Affirm all have official WooCommerce plugins |
| BigCommerce | Install from BigCommerce App Marketplace | All three providers have native BigCommerce apps |
| Custom / Headless | Enable via Stripe (Klarna, Afterpay) or provider SDK | If using Stripe, enable as payment methods on the Payment Intent; no separate SDK needed |
Provider comparison:
| Provider | Markets | Model | Merchant Fee | Order Range |
|---|---|---|---|---|
| Klarna | US, EU, UK | Pay in 4 / Financing | ~2.49% + $0.30 | $10–$10,000 |
| Afterpay | US, AU, UK, CA | Pay in 4 (6 weeks) | 4–6% | $35–$2,000 |
| Affirm | US, CA | Monthly installments | 5.99%–29.99% APR on consumer | $50–$17,500 |
Recommendation: Start with Klarna or Afterpay — they have the broadest platform support and lowest friction to enable. Add Affirm for high-ticket items ($1,000+) where monthly financing matters more than 4-installment splits.
Step 2: Enable BNPL on your platform
---
Shopify
Option A: Via Shopify Payments (recommended)
1. Go to Settings → Payments → Shopify Payments → Manage 2. Under Wallets and payment methods, find Klarna and Afterpay and toggle them on 3. Click Save — BNPL options will now appear automatically at checkout for eligible orders 4. To add BNPL messaging to product pages, install the Klarna On-Site Messaging app or Afterpay On-Site Messaging app from the Shopify App Store
Option B: Install the provider's Shopify app directly
- Klarna: Install Klarna Payments from the Shopify App Store
- Afterpay: Install Afterpay from the Shopify App Store
- Affirm: Install Affirm from the Shopify App Store
Each app guides you through account creation and automatically adds the payment method to your checkout.
Adding installment messaging to product pages (Shopify):
After installing the Klarna or Afterpay app, go to Online Store → Themes → Customize and add the BNPL messaging block to your product page template. This displays "4 interest-free payments of $X" beneath the price.
---
WooCommerce
Klarna: 1. Install the Klarna Payments for WooCommerce plugin from WordPress.org or the WooCommerce Marketplace 2. Go to WooCommerce → Settings → Payments → Klarna Payments and enter your Klarna API credentials 3. Enable the payment method and configure which Klarna products to offer (Pay Now, Pay Later, Slice It) 4. For product page messaging, install the Klarna On-Site Messaging for WooCommerce plugin and add your Client ID
Afterpay: 1. Install the Afterpay Gateway for WooCommerce plugin 2. Go to WooCommerce → Settings → Payments → Afterpay and enter your Merchant ID and Secret Key (from your Afterpay merchant portal) 3. Configure the minimum and maximum order amounts to match Afterpay's eligibility range ($35–$2,000) 4. Enable the on-site messaging widget to show installment amounts on product pages
Affirm: 1. Install the Affirm Gateway for WooCommerce plugin 2. Go to WooCommerce → Settings → Payments → Affirm and enter your Public API Key and Private API Key (from your Affirm merchant portal) 3. Set the minimum order threshold (Affirm recommends showing only for orders $50+)
---
BigCommerce
1. Go to Apps → Marketplace and search for Klarna, Afterpay, or Affirm 2. Install the provider's app and follow the in-app setup wizard — you will need your merchant account credentials from the provider's portal 3. The app automatically adds the BNPL payment method to your checkout and can add product page messaging 4. Configure eligibility amounts in the app settings to match the provider's supported range
---
Custom / Headless
Via Stripe (recommended — enables Klarna and Afterpay with no separate SDK):
// Server: include BNPL methods on the payment intent
const paymentIntent = await stripe.paymentIntents.create({
amount: orderTotalInCents,
currency: 'usd',
payment_method_types: ['card', 'klarna', 'afterpay_clearpay'],
metadata: { order_id: orderId },
});On the client, use Stripe's PaymentElement — it automatically shows Klarna and Afterpay tabs for eligible order amounts with no additional configuration:
import { PaymentElement } from '@stripe/react-stripe-js';
function CheckoutForm() {
return (
<form>
<PaymentElement options={{ layout: 'tabs' }} />
{/* Tabs render: Card | Klarna | Afterpay based on eligibility */}
</form>
);
}Adding installment messaging to product pages (headless):
Klarna provides a web component for price messaging. Load the Klarna script and add the component:
<!-- Load Klarna's on-site messaging library -->
<script async src="https://js.klarna.com/web-sdk/v1/klarna.js"
data-client-id="YOUR_KLARNA_CLIENT_ID">
</script>
<!-- Display "4 payments of $X" on the product page -->
<klarna-placement
data-key="credit-promotion-auto-size"
data-locale="en-US"
data-purchase-amount="9999">
<!-- Amount in cents: 9999 = $99.99 -->
</klarna-placement>Afterpay provides a similar web component:
<afterpay-placement
data-locale="en_US"
data-currency="USD"
data-amount="99.99"
data-size="sm">
</afterpay-placement>Step 3: Verify eligibility rules are enforced
Each provider has minimum and maximum order amounts. The platform apps and Stripe's PaymentElement handle this automatically — they only show the BNPL option when the order total is within the eligible range. Verify this is working correctly by testing with an order below the minimum (BNPL should not appear) and within range (BNPL should appear).
Best Practices
- Enable via Shopify Payments if you are on Shopify — zero extra integration; just toggle on in payment settings
- Show installment messaging on product pages and cart — BNPL messaging before checkout increases AOV even for customers who end up paying in full
- Only show BNPL within eligible amount ranges — displaying BNPL for a $10 order misleads customers; the platform apps handle this automatically
- Monitor fee impact on margin — Afterpay charges 4–6%, which is 2–3x higher than a standard card fee; ensure your margins support the provider's rate before enabling
- Display clear terms — "4 interest-free payments" must be accurate; each provider has compliance requirements for how you can describe their product
Common Pitfalls
| Problem | Solution |
|---|---|
| BNPL option not appearing at checkout | Check that the order amount is within the provider's eligible range; verify the API credentials in the plugin/app settings are for the correct environment (live vs. sandbox) |
| BNPL appearing for ineligible order amounts | Set minimum/maximum amount thresholds in the plugin settings; Stripe's PaymentElement enforces this automatically |
| Product page messaging not updating when variant price changes | Klarna and Afterpay web components read the data-purchase-amount attribute; update this attribute in JavaScript when the price changes |
| Customers confused about BNPL terms | Link to the provider's "How it works" page in the messaging widget; each provider has compliant messaging templates you must follow |
| Higher dispute rate with BNPL orders | BNPL disputes go through the provider (Klarna, Afterpay), not your payment processor; ensure your fulfillment process creates clear tracking and delivery confirmation |
Related Skills
- @stripe-integration
- @checkout-flow-optimization
- @paypal-integration
- @cart-logic
{
"context": "Tests whether the agent correctly implements the Afterpay direct JS SDK integration pattern including server-side checkout token creation (correct API endpoint, auth, headers, amount formatting), the two-step client-side redirect flow, proper click-handler wiring to avoid popup blocking, and server-side payment capture with idempotency.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Afterpay SDK script tag",
"max_score": 7,
"description": "The solution loads Afterpay JS SDK from 'https://js.afterpay.com/afterpay-1.x.js' (not a different URL or npm package)"
},
{
"name": "Server-side token endpoint",
"max_score": 8,
"description": "A server-side route/function creates the Afterpay checkout by POSTing to 'https://global-api.afterpay.com/v2/checkouts'"
},
{
"name": "Basic auth header",
"max_score": 9,
"description": "The Afterpay API request uses a Basic Authorization header constructed from AFTERPAY_MERCHANT_ID and AFTERPAY_SECRET_KEY (base64-encoded 'id:key')"
},
{
"name": "User-Agent header",
"max_score": 7,
"description": "The Afterpay API request includes a User-Agent header containing the store name and/or merchant ID"
},
{
"name": "Amount toFixed(2)",
"max_score": 8,
"description": "Monetary amount values sent to Afterpay's API are formatted with toFixed(2) or equivalent two-decimal-place formatting"
},
{
"name": "AfterPay.initialize called",
"max_score": 7,
"description": "The client-side code calls AfterPay.initialize({ countryCode: ... }) before launching the redirect"
},
{
"name": "AfterPay.redirect in sync click handler",
"max_score": 10,
"description": "AfterPay.redirect() is called synchronously within a click event handler — NOT inside an async function or after an await"
},
{
"name": "onComplete status check",
"max_score": 7,
"description": "The onComplete callback checks for status === 'SUCCESS' before proceeding to capture"
},
{
"name": "Server-side capture endpoint",
"max_score": 10,
"description": "A server-side route/function captures the payment by calling the Afterpay capture API (https://global-api.afterpay.com/v2/payments/capture) — capture is NOT assumed complete from the client callback alone"
},
{
"name": "Idempotency-Key on capture",
"max_score": 10,
"description": "The Afterpay capture API request includes an Idempotency-Key header set to the orderToken"
},
{
"name": "redirectConfirmUrl and redirectCancelUrl",
"max_score": 8,
"description": "The checkout creation payload includes merchant.redirectConfirmUrl and merchant.redirectCancelUrl"
},
{
"name": "Items array in checkout payload",
"max_score": 9,
"description": "The checkout creation payload includes an items array with name, sku, quantity, and price fields per line item"
}
]
}
Implement Afterpay Checkout on a Custom E-Commerce Platform
Problem/Feature Description
An outdoor gear retailer runs a custom-built Node.js + React storefront (not Stripe-based) and wants to add Afterpay as a checkout option. Their average order value sits around $180–$400 and they have noticed strong uptake of installment options among their 25–40 demographic in the US, Australia, and Canada. After signing an Afterpay merchant agreement, they have received their AFTERPAY_MERCHANT_ID and AFTERPAY_SECRET_KEY credentials and want the engineering team to wire up the full checkout flow.
The flow should allow a customer to click "Pay with Afterpay", be redirected to complete Afterpay's approval, then return to the store with the payment captured. The checkout page already has access to the cart contents including item names, SKUs, quantities, unit prices, and the customer's email and name. Credentials are available as environment variables AFTERPAY_MERCHANT_ID, AFTERPAY_SECRET_KEY, and STORE_URL.
Output Specification
Produce the following files:
server/afterpay.js— Two Express route handlers:
1. POST /api/afterpay/create-checkout — accepts { cartId } in the body, looks up the cart from the provided cart data below, and calls the Afterpay API to create a checkout session. Returns { token, redirectUrl }. 2. POST /api/afterpay/capture/:orderToken — captures the approved Afterpay payment for the given orderToken. Returns the capture result.
client/AfterpaButton.jsx— A React component with an "Pay with Afterpay" button. On click, it: (1) calls the create-checkout endpoint to get a token, then (2) launches the Afterpay flow. After customer approval, it calls the capture endpoint.workflow-log.md— A brief description of the integration steps your implementation follows, including any notable implementation decisions.
Input Files
The following cart data is provided for testing. Extract before beginning.
=============== FILE: inputs/sample-cart.json =============== { "cartId": "cart_8821", "email": "alex.morgan@example.com", "firstName": "Alex", "lastName": "Morgan", "total": 247.98, "items": [ { "title": "Trail Running Shoes", "sku": "TRS-2024-M10", "quantity": 1, "unitPrice": 189.99 }, { "title": "Merino Wool Hiking Socks (3-pack)", "sku": "MWS-3PK-L", "quantity": 2, "unitPrice": 28.995 } ] }
{
"context": "Tests whether the agent correctly implements BNPL eligibility filtering (amount ranges and country codes per provider), the Afterpay and Klarna messaging widget components with correct attributes, and the Klarna widget refresh behavior on price changes.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Afterpay min $35 / max $2000",
"max_score": 7,
"description": "The eligibility logic uses min=35 and max=2000 for Afterpay (not other values)"
},
{
"name": "Klarna min $10 / max $10000",
"max_score": 7,
"description": "The eligibility logic uses min=10 and max=10000 for Klarna (not other values)"
},
{
"name": "Affirm min $50 / max $17500",
"max_score": 7,
"description": "The eligibility logic uses min=50 and max=17500 for Affirm (not other values)"
},
{
"name": "Country filter per provider",
"max_score": 10,
"description": "The eligibility logic filters by both amount AND country code — providers are excluded when the customer's country is not in their supported list"
},
{
"name": "Afterpay placement element",
"max_score": 8,
"description": "The Afterpay messaging widget uses the <afterpay-placement> custom HTML element (not a div or img)"
},
{
"name": "Afterpay placement attributes",
"max_score": 8,
"description": "The <afterpay-placement> element includes data-locale, data-currency, and data-amount attributes"
},
{
"name": "Afterpay amount toFixed(2)",
"max_score": 6,
"description": "The data-amount attribute on <afterpay-placement> uses price.toFixed(2) or equivalent two-decimal formatting"
},
{
"name": "Klarna placement element",
"max_score": 8,
"description": "The Klarna messaging widget uses the <klarna-placement> custom HTML element"
},
{
"name": "Klarna data-key attribute",
"max_score": 8,
"description": "The <klarna-placement> element includes data-key='credit-promotion-auto-size'"
},
{
"name": "Klarna amount in cents",
"max_score": 10,
"description": "The data-purchase-amount on <klarna-placement> is in cents (e.g. Math.round(price * 100)), NOT in dollars"
},
{
"name": "Klarna refresh on price change",
"max_score": 9,
"description": "The Klarna component calls window.Klarna?.OnsiteMessaging?.refresh() (or Klarna.OnsiteMessaging.refresh()) when the price prop/value changes"
},
{
"name": "No widget shown outside range",
"max_score": 12,
"description": "The messaging components return null (or equivalent) when the price is below the provider's minimum or above the maximum"
}
]
}
Add BNPL Installment Messaging to Product and Cart Pages
Problem/Feature Description
A consumer electronics retailer sells products ranging from $8 phone cases to $4,000 laptops. They have recently enabled Klarna and Afterpay as checkout payment options but are not yet showing any installment messaging on their product detail pages or cart. The growth team has shared research indicating that displaying "pay over time" messaging before the customer reaches checkout meaningfully increases conversion and average order value — but only when the messaging is accurate and only displayed for qualifying order amounts.
The development team needs a set of React components that display installment messaging widgets from Afterpay and Klarna on the product page and in the cart. The components must handle the fact that product prices vary widely across the catalog, and that the store ships to multiple countries (US, UK, Australia, Canada, Germany). The components should only appear when the price and customer's country actually qualify for a given provider, and the Klarna widget must stay in sync when a customer switches between product variants with different prices.
Additionally, provide a utility function the rest of the codebase can use to determine which BNPL providers are available for a given order.
Output Specification
Produce the following files:
lib/bnplEligibility.js— A module exporting a function to determine which BNPL providers are available for a given order total and country code.components/AfterpayMessage.jsx— React component that shows the Afterpay installment widget for a given price. Should not render if the price is ineligible.components/KlarnaMessage.jsx— React component that shows the Klarna installment widget for a given price. Should stay in sync when the price prop changes. Should not render if the price is ineligible.components/BNPLMessaging.jsx— A wrapper component that acceptspriceandcountryCodeprops and renders the appropriate BNPL messaging widgets based on eligibility.bnpl-eligibility.test.js— Unit tests (using any test framework) that verify the eligibility function returns correct results for at least 5 different price/country combinations, including edge cases at the boundaries of provider ranges.
{
"context": "Tests whether the agent uses Stripe as the preferred BNPL integration path for a Stripe-based store, correctly adds BNPL payment method types to the PaymentIntent, and configures the PaymentElement UI appropriately — without requiring a separate BNPL SDK.",
"type": "weighted_checklist",
"checklist": [
{
"name": "No extra BNPL SDK",
"max_score": 10,
"description": "The solution does NOT install or import a separate Klarna, Afterpay, or Affirm SDK — relies solely on Stripe for BNPL"
},
{
"name": "klarna in payment_method_types",
"max_score": 10,
"description": "The server-side PaymentIntent creation includes 'klarna' in the payment_method_types array"
},
{
"name": "afterpay_clearpay in payment_method_types",
"max_score": 10,
"description": "The server-side PaymentIntent creation includes 'afterpay_clearpay' in the payment_method_types array (not 'afterpay')"
},
{
"name": "PaymentElement used",
"max_score": 10,
"description": "The frontend checkout form uses Stripe's PaymentElement component (not a custom card element or separate BNPL buttons)"
},
{
"name": "layout: tabs or accordion",
"max_score": 10,
"description": "The PaymentElement is configured with a layout option set to either 'tabs' or 'accordion'"
},
{
"name": "useStripe and useElements hooks",
"max_score": 8,
"description": "The frontend checkout component uses the useStripe() and useElements() hooks from @stripe/react-stripe-js"
},
{
"name": "PaymentIntent server-side",
"max_score": 10,
"description": "The PaymentIntent is created server-side (in a backend route/API handler), not on the client"
},
{
"name": "currency: 'usd'",
"max_score": 7,
"description": "The PaymentIntent includes currency: 'usd' (lowercase)"
},
{
"name": "amount in cents",
"max_score": 8,
"description": "The PaymentIntent amount is passed in cents (smallest currency unit, e.g. integer), not dollars"
},
{
"name": "BNPL eligibility range guard",
"max_score": 9,
"description": "The solution includes a check or comment indicating BNPL methods are only appropriate within an eligible order amount range (not shown/enabled for all order amounts)"
},
{
"name": "Card included",
"max_score": 8,
"description": "The payment_method_types array also includes 'card' alongside BNPL methods"
}
]
}
Add Installment Payment Options to Stripe Checkout
Problem/Feature Description
A mid-size furniture e-commerce brand is seeing 35% cart abandonment at checkout, and customer surveys indicate sticker shock on orders averaging $650. Marketing has data showing that customers in the $300–$1,800 range are especially price-sensitive. The engineering team wants to offer installment payment options alongside their existing credit card payments to help customers spread the cost over time. The store already processes all payments through Stripe and the team wants to avoid adding complexity from external SDKs.
Your task is to update the checkout backend and frontend to present installment payment options (Klarna, Afterpay, Affirm) to customers. The code should work within the existing Stripe setup — the backend creates a payment intent and the frontend renders a payment form using Stripe's React library. Keep the solution as straightforward as possible given the existing Stripe infrastructure.
Output Specification
Produce the following files:
server/create-payment-intent.js— The Express route handler (or equivalent) that creates the Stripe PaymentIntent for a checkout session. Include the order amount and relevant payment options.client/CheckoutForm.jsx— A React component that renders the payment form using Stripe's React SDK. Import from@stripe/react-stripe-js.README.md— Brief setup notes: what Stripe dashboard settings need to be enabled and any environment variables needed.
{
"name": "finsi/buy-now-pay-later",
"version": "0.1.0",
"summary": "Integrate BNPL providers (Klarna, Afterpay, Affirm) with eligibility checks",
"skills": {
"buy-now-pay-later": {
"path": "SKILL.md"
}
}
}