Ravelin's Browser SDK
ravelinjs is a JavaScript library for the browser to augment your integration
with:
* an identifier for the customer's browser to be attached to an order (core);
* simple page events like loading, pasting and resizing (track); and
* cardholder data encrypted for transmission through your server (encrypt).
Gathering these values accurately, and ensuring they are made available to
Ravelin through our API or by calls made directly from this SDK, is critical to
a successful Ravelin integration.
Please feel welcome to create issues or submit pull requests on the
project. The Contribution
Guidelines
detail how to write and test code for ravelinjs.
Note that this documentation is for version 1 of ravelinjs. For version 0,
please see its usage guide,
reference
and source.
* Quickstart
* Bundles
* npm
* Content-Security-Policy
* Script Integrity
* Browser Compatibility
* Examples
* deviceId Prefix
* API base URL
* Reference
* var ravelin = new Ravelin({cfg: object})
* ravelin.core.id(): Promise
* Device ID Format
* ravelin.encrypt.card(card: object): object
* ravelin.track.load()
* [ravelin.track.event(name, [props])](#ravelintrackeventname-props)
* ravelin.track.paste(event: ClipboardEvent)
* Vendored Code
* Upgrading
* Upgrading to ravelinjs v1 from ravelinjs v0
* Upgrading to ravelinjs v1 from cdn.ravelin.net script snippet
Add https://*.ravelin.click to your site's [Content-Security-Policyconnect-src][csp-connect] directive. Get a copy of
[ravelin-core+track+encrypt+promise.min.js on Github releases][releases] and
instantiate your Ravelin instance on the page:
``html`
> If you have a build system, you can instead install ravelinjs with
> npm using npm i ravelinjs@1 and require or`
> import Ravelin for instantiating:
>
> js`
> import Ravelin from 'ravelinjs/core+track+encrypt+promise';
> / or / const Ravelin = require('ravelinjs/core+track+encrypt+promise');
> var ravelin = new Ravelin({key: 'publishable_key_...'});
>
This will set the ravelinDeviceId cookie on your domain, send a page-load
event, and then allow you to call:
* ravelin.core.id().then(function(id) { ... }) to get the deviceId.ravelin.encrypt.card({pan: "4111 ..."})
* to encrypt cardholder data to be sentravelin.track.load()
to Ravelin.
* to track a page load.
If you are wanting to track paste events then lastly add a data-rvn-pandata-rvn-sensitive
attribute to any inputs the user types a credit/debit card number into, and a to or around any elements you don't want Ravelin to report
any content from.
Read on for more details.
*
The quickstart suggests using ravelin-core+track+encrypt+promise.min.js which
contains all functionality offered by ravelinjs and is therefore the easiest to
get started with but also the largest file. If you are not using all of the
functionality of ravelinjs you can choose a bundle with only the components you
need.
The components are:
* core: API and error-reporting functionality used by all bundles, and Basic
device identification with ravelin.core.id() or a ravelinDeviceId cookie.ravelin.encrypt.card()
* encrypt: Cardholder data encryption with .ravelin.track.load()
* track: Automatically send page-load, resize and paste events, or manually
with .
* promise: Provide a fallback Promise polyfill required for Internet
Explorer support. Optional if you already have your own polyfill or do not
want to support any version of Internet Explorer.
The [release files][releases] indicate which components they include using a
+component naming convention. For example, ravelin-core+track.min.js
contains only the core and track components and so cannot be used to encrypt
cards and doesn't guarantee Internet Explorer compatibility.
If you have a JavaScript build system and would prefer to include ravelinjs
using it, you can install ravelinjs from
npm with:
`bash`
npm install ravelinjs@1
You can then import the desired bundle within the ravelinjs library. For
example, to load the core+track bundle using require is:
`js`
var Ravelin = require('ravelinjs/core+track');
Or to load card encryption with ES6 imports is:
`js`
import Ravelin from 'ravelinjs/core+encrypt';
The bundles published to npm are in Universal Module Definition format.
RavelinJS will send track events and error reports back to the Ravelin API as
configured in the api initialisation property, or inferred from your API key.connect-src
If your site is configured with a Content-Security-Policy, be sure to add the
API to the directive:
`http`
Content-Security-Policy: connect-src 'self' https://*.ravelin.click;
If you are including a ravelin bundle directly on your page, rather than in your
build system, we recommended setting the integrity attribute on the script tag
to the corresponding value from the integrity file of the release. For example,
if the integrity file reads:
sha384-8de9e022e2f67e2072bb114e670d2fb37cab8eaf81616bcc3951087aa473e62a8b9fcc4c780a8d8d09df55c8b63bfd7c ravelin-1.0.0-rc1-core+promise.js
then your HTML becomes:
`html`
If you are using a modern bundler and transpiler you can declare:
`js`
const deviceId = await ravelin.core.id();
Server-side example:
`js`
var card = JSON.parse(form.getValue('card-cipher'));
var action = fetch('https://api.ravelin.com/v2/checkout?score=checkoutPreAuth', {
method: 'POST',
headers: {...},
body: JSON.stringify({
timestamp: (new Date).getTime(),
customerId: customerId,
order: {...},
device: {
deviceId: form.getValue('device-id'),
userAgent: req.header('User-Agent'),
ipAddress: req.ip, // X-Forwarded-For in Express JS.
language: req.header('Accept-Language'),
}
})
});
#### Device ID Format
The device ID in the ravelinDeviceId cookie or returned by ravelin.core.id()
should be treated as an opaque string. Do not attempt to parse or validate the
format of the ID as we may change it without warning in the future.
ravelin.encrypt.card returns an object describing the encrypted form of
cardholder data for use with Ravelin's client-side
encryption.
This object can then be sent via your server to Ravelin without increasing the
scope of PCI compliance required of your server. The object can be used directly
as a paymentMethod in a [v2/checkout][postv2checkout],
[v2/paymentmethod][postv2paymentmethod] or [v2/connect][postv2connect] request,
for example.
Encrypting cardholder data is only necessary for non-PCI compliant merchants (PCI
SAQ-A or SAQ-AEP merchants) who are otherwise unable to provide cardholder data
(including a valid
instrumentId)
to Ravelin when scoring an order.
The full set of fields are:
`js`
var cipher = ravelin.encrypt.card({
/* @prop {string} pan The full primary account number of the card. /
pan: '4111 1111 1111 1111',
/* @prop {string|number} year The expiry year on the card. 12 => 2012. /
year: '2020',
/* @prop {string|number} month The expiry month on the card. 1 => Jan. /
month: '1',
/* @prop {string} [nameOnCard] Optional cardholder name. /
nameOnCard: 'Tom Johnson'
/* @prop {string} [rsaKey] Optional RSA public key to use. Can be set during instantiation. /
// rsaKey: '0|...',
});
HTML example:
`html`
Server-side usage example:
`js`
var card = JSON.parse(form.getValue('card-cipher'));
var action = fetch('https://api.ravelin.com/v2/checkout?score=true', {
method: 'POST',
headers: {...},
body: JSON.stringify({
timestamp: (new Date).getTime(),
customer: {...},
order: {...},
transaction: {...},
paymentMethod: card,
device: {...}
})
});
Note that browsers which do not support
window.crypto (including IE8-IE10) rely on
a pseudo-random number generator based on collecting user events from the page
and that if this generator has not collected enough events it may throw an
exception when trying to encrypt.
Send a page-load event. This is automatically triggered when Ravelin is
instantiated, but should be invoked manually after page navigation in a
single-page app. To ensure the correct page title is collected, call after the
page content has loaded - so the [Window popstate][popstate] event may be too
early.
Send a named event to attach to the session, with optional descriptive
properties. Most event names use "UPPER\_SNAKE\_CASE" but the most important thing
is to have consistency between your browser and mobile applications where they
have common events. Returns a Promise that resolves once the event has been
sent.
Send a paste event to Ravelin. This is done automatically if the paste happens
in the same frame Ravelin is instantiated - except on IE8 which does not support
paste-event listening at the document level.
To correctly identify the paste contents you should annotate your forms with
attributes:
* data-rvn-pan if the user enters a credit-card number into that input; ordata-rvn-sensitive
* if no values should be shared in the event back to Ravelin.
> Note: It is possible to override these attributes by providing custom classifyPaste logic in your Ravelin instance. See Reference.
The paste event contains information about where the paste happened and
approximate shape of the paste content. For example, if a user pastes "h3ll0,
wor1d." into a field, Ravelin will receive "X0XX0, XXX0X.". However, if the
pasted content is an , a or
a child of any
(if using the default attributes) field we will not include any form of
pasted value - only that a paste event occurred.Vendored Code
This library would not have been possible without the stellar works upon which
it relies:
* http://bitwiseshiftleft.github.io/sjcl/ (MIT)
* http://www-cs-students.stanford.edu/~tjw/jsbn/ (BSD)
* https://github.com/Joe12387/detectIncognito (MIT)
Upgrading
Note that the format of the deviceId was changed in v1 to include a "rjs-"
prefix. If you do any validation or parsing that checks for a particular
format of the deviceId, please remove this logic and
instead treat the deviceId as an opaque string.
$3
If you are using RavelinJS v0 from a script or loaded via npm then equivalent
functionality is now covered by bundles with the core+track+encrypt components.
Please review which components you need in the bundles and complete
the [quickstart] setup instructions. You can now remove cdn.ravelin.net from
your Content-Security-Policy and make the following substitutions to complete
the upgrade:
*
ravelinjs.setFallbackJS(src) → Removed.
* ravelinjs.setCookieDomain(domain) → Set during instantiation in new.
* ravelinjs.setPublicAPIKey(apiKey) → Set during instantiation in new Ravelin({key: apiKey}).
* ravelinjs.setRSAKey(rawPubKey) → Set during instantiation in new Ravelin({rsaKey: rawPubKey}).
* ravelinjs.setCustomerId(customerId) → Removed.
* ravelinjs.setTempCustomer → Removed.
* ravelinjs.encrypt(card) → JSON.stringify(ravelin.encrypt.card(card))
* ravelinjs.encryptAsObject(card) → ravelin.encrypt.card(card)
* ravelinjs.track(eventName, meta) → Removed.
* ravelinjs.trackPage(meta) → ravelin.track.load()
* ravelinjs.trackLogout(meta) → Removed.
* ravelinjs.trackFingerprint(customer) → Removed. This method implemented some
privacy-insensitive browser fingerprinting that Ravelin no longer wishes to be
part of. Instead, follow the instructions of
[ravelin.core.id()][ravelin.core.id] to send the device via your server.
* ravelinjs.setOrderId(orderId) → Removed.$3
If you previously used a snippet such as
`js
(function(r,a,v,e,l,i,n){r[l]=r[l]||function(){(r[l].q=r[l].q||[]).push(arguments)};i=a.createElement(v);i.async=i.defer=1;i.src=e;a.body.appendChild(i)})(window,document,'script','https://cdn.ravelin.net/ravelin-beta.min.js','ravelin');
`or
`js
(function r(a,v,e,l,i,n){a[e]=a[e]||function(){(a[e].q=a[e].q||[]).push(arguments)};n=v.createElement("script");n.async=n.defer=1;n.src=l;if(i)n.onerror=function(){r(a,v,e,i)};v.body.appendChild(n)})(window,document,"ravelin","https://cdn.ravelin.net/js/rvn-beta.min.js","/rvn-lite.min.js")
`then the functionality you were using is covered by bundles with the core+track
components. After following the [quickstart] instructions you can remove
cdn.ravelin.net from your Content-Security-Policy, and make the following
substitutions to complete the upgrade:
*
ravelin('setApiKey', 'k') → Set during instantiation in new Ravelin({key: 'k'}).
* ravelin('setCookieDomain', 'c') → Set during instantiation in new.
* ravelin('track') → Removed.
* ravelin('trackPage') → [ravelin.track.load()][ravelin.track.load] is now
called when Ravelin is instantiated, but you can call this method again when
the user navigates if you have a single-page application.
* ravelin('trackLogin') → Removed.
* ravelin('trackLogout') → Removed.
* ravelin('fingerprint') → Removed. This method implemented some
privacy-insensitive browser fingerprinting that Ravelin no longer wishes to be
part of. Instead, follow the instructions of
[ravelin.core.id()][ravelin.core.id] to send the device via your server.
* ravelin('send') → Removed
* ravelin('setCustomerId') → Removed.
* ravelin('setTempCustomerId') → Removed.
* ravelin('setOrderId')` → Removed.[ravelin.encrypt.card]: #ravelinencryptcardcard-object-object
[ravelin.track.load]: #ravelintrackload
[ravelin.core.id]: #ravelincoreid-promisestring
[quickstart]: #quickstart "RavelinJS Quickstart Instructions"
[releases]: https://www.github.com/unravelin/ravelinjs/releases "RavelinJS GitHub Releases"
[postv2checkout]: https://developer.ravelin.com/merchant/api/endpoints/checkout/ "Ravelin API: POST /v2/checkout"
[postv2paymentmethod]: https://developer.ravelin.com/merchant/api/endpoints/payment-method/ "Ravelin API: POST /v2/paymentmethod"
[postv2connect]: https://developer.ravelin.com/merchant/api/endpoints/connect/ "Ravelin Connect API: POST /v2/connect"
[Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise "MDN: JavaScript Promises"
[popstate]: https://developer.mozilla.org/en-US/docs/Web/API/Window/popstate_event "MDN: Window popstate event"
[csp-connect]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/connect-src "MDN: Content-Security-Policy connect-src"