mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-01 07:02:48 +08:00
✨ New LocalStorage module for Google Pay
This commit is contained in:
parent
cf83d2ba6e
commit
451dc842ed
2 changed files with 210 additions and 0 deletions
179
modules/ppcp-button/resources/js/modules/Helper/LocalStorage.js
Normal file
179
modules/ppcp-button/resources/js/modules/Helper/LocalStorage.js
Normal file
|
@ -0,0 +1,179 @@
|
|||
/* global localStorage */
|
||||
|
||||
function checkLocalStorageAvailability() {
|
||||
try {
|
||||
const testKey = '__ppcp_test__';
|
||||
localStorage.setItem( testKey, 'test' );
|
||||
localStorage.removeItem( testKey );
|
||||
return true;
|
||||
} catch ( e ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function sanitizeKey( name ) {
|
||||
return name
|
||||
.toLowerCase()
|
||||
.trim()
|
||||
.replace( /[^a-z0-9_-]/g, '_' );
|
||||
}
|
||||
|
||||
function deserializeEntry( serialized ) {
|
||||
try {
|
||||
const payload = JSON.parse( serialized );
|
||||
|
||||
return {
|
||||
data: payload.data,
|
||||
expires: payload.expires || 0,
|
||||
};
|
||||
} catch ( e ) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function serializeEntry( data, timeToLive ) {
|
||||
const payload = {
|
||||
data,
|
||||
expires: calculateExpiration( timeToLive ),
|
||||
};
|
||||
|
||||
return JSON.stringify( payload );
|
||||
}
|
||||
|
||||
function calculateExpiration( timeToLive ) {
|
||||
return timeToLive ? Date.now() + timeToLive * 1000 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* A reusable class for handling data storage in the browser's local storage,
|
||||
* with optional expiration.
|
||||
*
|
||||
* Can be extended for module specific logic.
|
||||
*
|
||||
* @see GooglePaySession
|
||||
*/
|
||||
export class LocalStorage {
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
#group = '';
|
||||
|
||||
/**
|
||||
* @type {null|boolean}
|
||||
*/
|
||||
#canUseLocalStorage = null;
|
||||
|
||||
/**
|
||||
* @param {string} group - Group name for all storage keys managed by this instance.
|
||||
*/
|
||||
constructor( group ) {
|
||||
this.#group = sanitizeKey( group ) + ':';
|
||||
this.#removeExpired();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all items in the current group that have reached the expiry date.
|
||||
*/
|
||||
#removeExpired() {
|
||||
if ( ! this.canUseLocalStorage ) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object.keys( localStorage ).forEach( ( key ) => {
|
||||
if ( ! key.startsWith( this.#group ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const entry = deserializeEntry( localStorage.getItem( key ) );
|
||||
if ( entry && entry.expires > 0 && entry.expires < Date.now() ) {
|
||||
localStorage.removeItem( key );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes the given entry name and adds the group prefix.
|
||||
*
|
||||
* @throws {Error} If the name is empty after sanitization.
|
||||
* @param {string} name - Entry name.
|
||||
* @return {string} Prefixed and sanitized entry name.
|
||||
*/
|
||||
#entryKey( name ) {
|
||||
const sanitizedName = sanitizeKey( name );
|
||||
|
||||
if ( sanitizedName.length === 0 ) {
|
||||
throw new Error( 'Name cannot be empty after sanitization' );
|
||||
}
|
||||
|
||||
return `${ this.#group }${ sanitizedName }`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates, whether localStorage is available.
|
||||
*
|
||||
* @return {boolean} True means the localStorage API is available.
|
||||
*/
|
||||
get canUseLocalStorage() {
|
||||
if ( null === this.#canUseLocalStorage ) {
|
||||
this.#canUseLocalStorage = checkLocalStorageAvailability();
|
||||
}
|
||||
|
||||
return this.#canUseLocalStorage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores data in the browser's local storage, with an optional timeout.
|
||||
*
|
||||
* @param {string} name - Name of the item in the storage.
|
||||
* @param {any} data - The data to store.
|
||||
* @param {number} [timeToLive=0] - Lifespan in seconds. 0 means the data won't expire.
|
||||
* @throws {Error} If local storage is not available.
|
||||
*/
|
||||
set( name, data, timeToLive = 0 ) {
|
||||
if ( ! this.canUseLocalStorage ) {
|
||||
throw new Error( 'Local storage is not available' );
|
||||
}
|
||||
|
||||
const entry = serializeEntry( data, timeToLive );
|
||||
const entryKey = this.#entryKey( name );
|
||||
|
||||
localStorage.setItem( entryKey, entry );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves previously stored data from the browser's local storage.
|
||||
*
|
||||
* @param {string} name - Name of the stored item.
|
||||
* @return {any|null} The stored data, or null when no valid entry is found or it has expired.
|
||||
* @throws {Error} If local storage is not available.
|
||||
*/
|
||||
get( name ) {
|
||||
if ( ! this.canUseLocalStorage ) {
|
||||
throw new Error( 'Local storage is not available' );
|
||||
}
|
||||
|
||||
const itemKey = this.#entryKey( name );
|
||||
const entry = deserializeEntry( localStorage.getItem( itemKey ) );
|
||||
|
||||
if ( ! entry ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return entry.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified entry from the browser's local storage.
|
||||
*
|
||||
* @param {string} name - Name of the stored item.
|
||||
* @throws {Error} If local storage is not available.
|
||||
*/
|
||||
clear( name ) {
|
||||
if ( ! this.canUseLocalStorage ) {
|
||||
throw new Error( 'Local storage is not available' );
|
||||
}
|
||||
|
||||
const itemKey = this.#entryKey( name );
|
||||
localStorage.removeItem( itemKey );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
import { LocalStorage } from '../../../../ppcp-button/resources/js/modules/Helper/LocalStorage';
|
||||
|
||||
export class GooglePayStorage extends LocalStorage {
|
||||
static PAYER = 'payer';
|
||||
static PAYER_TTL = 900; // 15 minutes in seconds
|
||||
|
||||
constructor() {
|
||||
super( 'ppcp-googlepay' );
|
||||
}
|
||||
|
||||
getPayer() {
|
||||
return this.get( GooglePayStorage.PAYER );
|
||||
}
|
||||
|
||||
setPayer( data ) {
|
||||
/*
|
||||
* The payer details are deleted on successful checkout, or after the TTL is reached.
|
||||
* This helps to remove stale data from the browser, in case the customer chooses to
|
||||
* use a different method to complete the purchase.
|
||||
*/
|
||||
this.set( GooglePayStorage.PAYER, data, GooglePayStorage.PAYER_TTL );
|
||||
}
|
||||
|
||||
clearPayer() {
|
||||
this.clear( GooglePayStorage.PAYER );
|
||||
}
|
||||
}
|
||||
|
||||
const moduleStorage = new GooglePayStorage();
|
||||
|
||||
export default moduleStorage;
|
Loading…
Add table
Add a link
Reference in a new issue