Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.papp.sa/llms.txt

Use this file to discover all available pages before exploring further.

Points exposes a single embeddable widget loader that renders promotional messaging in isolated iframes. One loader, one initializer, one type parameter — used on product pages and checkout pages alike. This page covers everything you need to integrate: surfaces, initialization, configuration, do/don’t guidance, and CSP.

Available surfaces

Surfacetype value(s)Where to place
Product page — promo messagepoints-alert, points-cardDirectly below the product price, close to the buy CTA
Checkout — installments bannerinstallmentsAbove payment methods, before the final CTA

Product page preview

Product widget preview When points-card is clicked, it can open a richer parent-page modal: Product widget modal

Checkout page preview

Checkout widget preview

Script source

Use the current loader on every surface:
<script src="https://business.papp.sa/widget-loader.js"></script>

Quick start — product page

<div id="points-product-widget"></div>
<script src="https://business.papp.sa/widget-loader.js"></script>
<script>
  PointsWidget.init({
    type: 'points-card',
    lang: 'ar',
    points: 250,
    container: '#points-product-widget'
  });
</script>

Quick start — checkout

<div id="points-checkout-widget"></div>
<script src="https://business.papp.sa/widget-loader.js"></script>
<script>
  PointsWidget.init({
    type: 'installments',
    lang: 'ar',
    container: '#points-checkout-widget',
    callbackUrl: 'https://merchant.example.com/checkout/return'
  });
</script>

Configuration

All surfaces share the same init() options. Only the type value differs.
type
string
required
One of points-alert, points-card (product surfaces), or installments (checkout surface).
lang
string
default:"ar"
ar for Arabic storefronts (default), en for English.
points
number
default:"0"
Numeric points value rendered by the promo widgets (points-alert / points-card). Ignored by installments.
container
string | HTMLElement
required
Target container selector or DOM element where the iframe wrapper will be mounted.
callbackUrl
string
Used by the installments flow when continuing shopping outside mobile webviews.
widgetUrl
string
Optional widget base URL. If omitted, the loader infers it from the script source.
parentOrigin
string
Optional postMessage origin override. Auto-detected when omitted.

Variants explained

A compact inline message. In the current implementation it can be dismissed and does not open a modal.
A richer promotional card that can open a parent-page popup with more detail.
Checkout-stage promotional banner. Honors callbackUrl for the “continue shopping” action on mobile webviews.

Auto-init from script attributes

The loader also supports data-attribute initialization, useful when you cannot run inline JS:
<div id="product-widget"></div>
<script
  src="https://business.papp.sa/widget-loader.js"
  data-type="points-alert"
  data-lang="ar"
  data-points="300"
  data-container="#product-widget">
</script>

Cleanup

The loader exposes a destroy() method for SPAs re-rendering containers:
PointsWidget.destroy();
This removes the wrapper and unregisters the postMessage listener.

Implementation guidelines

Product surfaces

Do:
  • Place points-alert or points-card directly below the product price or close to the primary buy CTA.
  • Use lang: 'ar' on Arabic pages, lang: 'en' on English pages.
  • Leave enough vertical space for the preset iframe height.
  • Test click behavior for points-card so the parent modal is not blocked by other overlays.
Do not:
  • Do not expect points-alert to open a modal — it’s dismissible by design.
  • Do not initialize the widget before its container exists in the DOM.
  • Do not hard-code a container height lower than the widget’s preset height.

Checkout surface

Do:
  • Use installments for the current checkout-stage implementation.
  • Place it before payment or decision-critical content.
  • Verify callbackUrl behavior in your real checkout flow.
Do not:
  • Do not ship unsupported callbacks (e.g. onRedeem()) — they don’t exist in the current loader.
  • Do not assume widget behavior from generic BNPL docs; validate against the real loader.

Content Security Policy

If your storefront uses CSP, allow the widget origin and iframe endpoints:
script-src 'self' https://business.papp.sa;
frame-src 'self' https://business.papp.sa;
connect-src 'self' https://business.papp.sa;
img-src 'self' data: https:;
font-src 'self' data: https://business.papp.sa;
style-src 'self' 'unsafe-inline' https://business.papp.sa;
The widget backend itself enforces security headers on /widget/embed and /widget/modal, including CSP, X-Content-Type-Options, X-XSS-Protection, and Referrer-Policy.

What is not currently supported

To set expectations honestly — these are not part of today’s loader:
  • theme switching API
  • size variants as public config
  • CSS variable API
  • a dedicated checkout redemption callback
  • separate widget SDK classes such as PointsProductWidget or PointsCheckoutWidget
Keep your integration aligned with the loader behavior documented here. If the widget API expands later, those options will be added only after they exist in production.

Current loader behavior worth testing

  • iframe renders with a skeleton loader first
  • height changes are driven by postMessage resize events
  • modal content opens in the parent page for points-card
  • destroy() removes the wrapper and unregisters the message listener
  • auto-init is supported through script data-* attributes

Pre-launch checks

  • Widget script loads from the intended environment
  • type matches the actual surface (points-alert, points-card, or installments)
  • Arabic pages render RTL correctly
  • Modal works on mobile and desktop
  • CSP does not block fonts, images, iframe, or script loading
  • Checkout-stage widget uses a valid callbackUrl when needed

Troubleshooting

Expected behavior. The current implementation is intentionally dismissible and does not open a modal.
Check that the container exists at init time and that no CSP rule is blocking the script or iframe origin.

Next

Widget Playground

Live, editable preview to try the loader before integrating.