Skip to content

Commit e01f703

Browse files
amittapallirasbhat
authored andcommitted
W-21354759: Implement SF Payments support in create app package and remove private client default
1 parent 998b5e2 commit e01f703

File tree

17 files changed

+307
-60
lines changed

17 files changed

+307
-60
lines changed

packages/commerce-sdk-react/package-lock.json

Lines changed: 4 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/commerce-sdk-react/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
"version": "node ./scripts/version.js"
4141
},
4242
"dependencies": {
43-
"commerce-sdk-isomorphic": "5.0.0",
43+
"commerce-sdk-isomorphic": "5.1.0-unstable-20260226081656",
4444
"js-cookie": "^3.0.1",
4545
"jwt-decode": "^4.0.0"
4646
},

packages/pwa-kit-create-app/assets/bootstrap/js/config/default.js.hbs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,15 @@ module.exports = {
157157
groupBonusProductsWithQualifyingProduct: true
158158
}
159159
},
160+
// Salesforce Payments configuration
161+
// Set enabled to false to disable Salesforce Payments even if the Commerce Cloud instance supports it.
162+
// Example URLs: sdkUrl: 'https://<instance>.unified.demandware.net/on/demandware.static/Sites-Site/-/-/internal/jscript/sfp/v1/sfp.js'
163+
// metadataUrl: 'https://<instance>.unified.demandware.net/on/demandware.static/Sites-Site/-/-/internal/metadata/v1.json'
164+
sfPayments: {
165+
enabled: true,
166+
sdkUrl: '',
167+
metadataUrl: ''
168+
},
160169
// Google Cloud api config
161170
googleCloudAPI: {
162171
apiKey: process.env.GOOGLE_CLOUD_API_KEY

packages/pwa-kit-create-app/assets/bootstrap/js/overrides/app/ssr.js.hbs

Lines changed: 79 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import crypto from 'crypto'
1111
import express from 'express'
1212
import helmet from 'helmet'
13+
import https from 'https'
1314
import {createRemoteJWKSet as joseCreateRemoteJWKSet, jwtVerify, decodeJwt} from 'jose'
1415
import path from 'path'
1516
import {getRuntime} from '@salesforce/pwa-kit-runtime/ssr/server/express'
@@ -345,14 +346,21 @@ const {handler} = runtime.createHandler(options, (app) => {
345346
'img-src': [
346347
// Default source for product images - replace with your CDN
347348
'*.commercecloud.salesforce.com',
348-
'*.demandware.net'
349+
'*.demandware.net',
350+
'*.adyen.com' // Payment gateways
349351
],
350352
'script-src': [
351353
// Used by the service worker in /worker/main.js
352354
'storage.googleapis.com',
353355
// Connect to Google Cloud APIs
354356
'maps.googleapis.com',
355-
'places.googleapis.com'
357+
'places.googleapis.com',
358+
// Payment gateways
359+
'*.stripe.com',
360+
'*.paypal.com',
361+
'*.adyen.com',
362+
'*.google.com',
363+
'*.demandware.net', // Used to load a valid payment scripts in test environment
356364
],
357365
'connect-src': [
358366
// Connect to Einstein APIs
@@ -363,11 +371,20 @@ const {handler} = runtime.createHandler(options, (app) => {
363371
'maps.googleapis.com',
364372
'places.googleapis.com',
365373
// Connect to SCRT2 URLs
366-
'*.salesforce-scrt.com'
374+
'*.salesforce-scrt.com',
375+
// Payment gateways
376+
'*.demandware.net', // Used to load a valid payment scripts in test environment
377+
'*.adyen.com',
378+
'*.google.com'
367379
],
368380
'frame-src': [
369381
// Allow frames from Salesforce site.com (Needed for MIAW)
370-
'*.site.com'
382+
'*.site.com',
383+
// Payment gateways
384+
'*.stripe.com',
385+
'*.paypal.com',
386+
'*.google.com',
387+
'*.adyen.com'
371388
]
372389
}
373390
}
@@ -429,6 +446,64 @@ const {handler} = runtime.createHandler(options, (app) => {
429446
app.get('/favicon.ico', runtime.serveStaticFile('static/ico/favicon.ico'))
430447

431448
app.get('/worker.js(.map)?', runtime.serveServiceWorker)
449+
450+
// Helper function to transform relative icon paths to absolute URLs
451+
function transformIconPaths(data, ecomServerHost) {
452+
const baseUrl = `https://${ecomServerHost}/on/demandware.static/Sites-Site/-/-/internal`
453+
const dataStr = JSON.stringify(data)
454+
// Replace all relative icon paths with absolute URLs
455+
const transformedStr = dataStr.replace(/"src":\s*"\/icons\//g, `"src":"${baseUrl}/icons/`)
456+
return JSON.parse(transformedStr)
457+
}
458+
459+
app.get('/api/payment-metadata', async (req, res) => {
460+
try {
461+
// Parse the URL to extract hostname and path
462+
const url = new URL(config.app.sfPayments.metadataUrl)
463+
// Use Node's https module instead of fetch
464+
const data = await new Promise((resolve, reject) => {
465+
const options = {
466+
hostname: url.hostname,
467+
path: url.pathname,
468+
method: 'GET',
469+
rejectUnauthorized: false, // This bypasses SSL verification
470+
headers: {
471+
Accept: 'application/json'
472+
}
473+
}
474+
475+
const req = https.request(options, (response) => {
476+
let data = ''
477+
response.on('data', (chunk) => {
478+
data += chunk
479+
})
480+
response.on('end', () => {
481+
try {
482+
resolve(JSON.parse(data))
483+
} catch (e) {
484+
reject(e)
485+
}
486+
})
487+
})
488+
489+
req.on('error', reject)
490+
req.end()
491+
})
492+
493+
// Transform relative icon paths to absolute URLs
494+
const transformedData = transformIconPaths(data, url.hostname)
495+
496+
res.setHeader('Access-Control-Allow-Origin', '*')
497+
res.setHeader('Content-Type', 'application/json')
498+
res.json(transformedData)
499+
} catch (error) {
500+
res.status(500).json({
501+
error: 'Failed to fetch metadata',
502+
details: error.message
503+
})
504+
}
505+
})
506+
432507
app.get('*', runtime.render)
433508
})
434509
// SSR requires that we export a single handler function called 'get', that

packages/pwa-kit-create-app/assets/templates/@salesforce/retail-react-app/app/ssr.js.hbs

Lines changed: 79 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import crypto from 'crypto'
1111
import express from 'express'
1212
import helmet from 'helmet'
13+
import https from 'https'
1314
import {createRemoteJWKSet as joseCreateRemoteJWKSet, jwtVerify, decodeJwt} from 'jose'
1415
import path from 'path'
1516
import {getRuntime} from '@salesforce/pwa-kit-runtime/ssr/server/express'
@@ -345,14 +346,21 @@ const {handler} = runtime.createHandler(options, (app) => {
345346
'img-src': [
346347
// Default source for product images - replace with your CDN
347348
'*.commercecloud.salesforce.com',
348-
'*.demandware.net'
349+
'*.demandware.net',
350+
'*.adyen.com' // Payment gateways
349351
],
350352
'script-src': [
351353
// Used by the service worker in /worker/main.js
352354
'storage.googleapis.com',
353355
// Connect to Google Cloud APIs
354356
'maps.googleapis.com',
355-
'places.googleapis.com'
357+
'places.googleapis.com',
358+
// Payment gateways
359+
'*.stripe.com',
360+
'*.paypal.com',
361+
'*.adyen.com',
362+
'*.google.com',
363+
'*.demandware.net', // Used to load a valid payment scripts in test environment
356364
],
357365
'connect-src': [
358366
// Connect to Einstein APIs
@@ -363,11 +371,20 @@ const {handler} = runtime.createHandler(options, (app) => {
363371
'maps.googleapis.com',
364372
'places.googleapis.com',
365373
// Connect to SCRT2 URLs
366-
'*.salesforce-scrt.com'
374+
'*.salesforce-scrt.com',
375+
// Payment gateways
376+
'*.demandware.net', // Used to load a valid payment scripts in test environment
377+
'*.adyen.com',
378+
'*.google.com'
367379
],
368380
'frame-src': [
369381
// Allow frames from Salesforce site.com (Needed for MIAW)
370-
'*.site.com'
382+
'*.site.com',
383+
// Payment gateways
384+
'*.stripe.com',
385+
'*.paypal.com',
386+
'*.google.com',
387+
'*.adyen.com'
371388
]
372389
}
373390
}
@@ -429,6 +446,64 @@ const {handler} = runtime.createHandler(options, (app) => {
429446
app.get('/favicon.ico', runtime.serveStaticFile('static/ico/favicon.ico'))
430447

431448
app.get('/worker.js(.map)?', runtime.serveServiceWorker)
449+
450+
// Helper function to transform relative icon paths to absolute URLs
451+
function transformIconPaths(data, ecomServerHost) {
452+
const baseUrl = `https://${ecomServerHost}/on/demandware.static/Sites-Site/-/-/internal`
453+
const dataStr = JSON.stringify(data)
454+
// Replace all relative icon paths with absolute URLs
455+
const transformedStr = dataStr.replace(/"src":\s*"\/icons\//g, `"src":"${baseUrl}/icons/`)
456+
return JSON.parse(transformedStr)
457+
}
458+
459+
app.get('/api/payment-metadata', async (req, res) => {
460+
try {
461+
// Parse the URL to extract hostname and path
462+
const url = new URL(config.app.sfPayments.metadataUrl)
463+
// Use Node's https module instead of fetch
464+
const data = await new Promise((resolve, reject) => {
465+
const options = {
466+
hostname: url.hostname,
467+
path: url.pathname,
468+
method: 'GET',
469+
rejectUnauthorized: false, // This bypasses SSL verification
470+
headers: {
471+
Accept: 'application/json'
472+
}
473+
}
474+
475+
const req = https.request(options, (response) => {
476+
let data = ''
477+
response.on('data', (chunk) => {
478+
data += chunk
479+
})
480+
response.on('end', () => {
481+
try {
482+
resolve(JSON.parse(data))
483+
} catch (e) {
484+
reject(e)
485+
}
486+
})
487+
})
488+
489+
req.on('error', reject)
490+
req.end()
491+
})
492+
493+
// Transform relative icon paths to absolute URLs
494+
const transformedData = transformIconPaths(data, url.hostname)
495+
496+
res.setHeader('Access-Control-Allow-Origin', '*')
497+
res.setHeader('Content-Type', 'application/json')
498+
res.json(transformedData)
499+
} catch (error) {
500+
res.status(500).json({
501+
error: 'Failed to fetch metadata',
502+
details: error.message
503+
})
504+
}
505+
})
506+
432507
app.get('*', runtime.render)
433508
})
434509
// SSR requires that we export a single handler function called 'get', that

packages/pwa-kit-create-app/assets/templates/@salesforce/retail-react-app/config/default.js.hbs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,15 @@ module.exports = {
157157
groupBonusProductsWithQualifyingProduct: true
158158
}
159159
},
160+
// Salesforce Payments configuration
161+
// Set enabled to false to disable Salesforce Payments even if the Commerce Cloud instance supports it.
162+
// Example URLs: sdkUrl: 'https://<instance>.unified.demandware.net/on/demandware.static/Sites-Site/-/-/internal/jscript/sfp/v1/sfp.js'
163+
// metadataUrl: 'https://<instance>.unified.demandware.net/on/demandware.static/Sites-Site/-/-/internal/metadata/v1.json'
164+
sfPayments: {
165+
enabled: true,
166+
sdkUrl: '',
167+
metadataUrl: ''
168+
},
160169
// Google Cloud api config
161170
googleCloudAPI: {
162171
apiKey: process.env.GOOGLE_CLOUD_API_KEY

packages/template-retail-react-app/app/components/_app-config/index.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ const AppConfig = ({children, locals = {}}) => {
107107
defaultDnt={DEFAULT_DNT_STATE}
108108
// Set 'enablePWAKitPrivateClient' to true to use SLAS private client login flows.
109109
// Make sure to also enable useSLASPrivateClient in ssr.js when enabling this setting.
110-
enablePWAKitPrivateClient={true}
110+
enablePWAKitPrivateClient={false}
111111
privateClientProxyEndpoint={slasPrivateClientProxyEndpoint}
112112
// Uncomment 'hybridAuthEnabled' if the current site has Hybrid Auth enabled. Do NOT set this flag for hybrid storefronts using Plugin SLAS.
113113
// hybridAuthEnabled={true}

packages/template-retail-react-app/app/components/forms/useAddressFields.jsx

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -288,40 +288,50 @@ export default function useAddressFields({
288288
name: `${prefix}stateCode`,
289289
label: formatMessage(countryCode === 'CA' ? messages.province : messages.state),
290290
defaultValue: '',
291-
type: 'select',
292-
options: [
293-
{value: '', label: ''},
294-
...(countryCode === 'CA' ? provinceOptions : stateOptions)
295-
],
291+
type: countryCode === 'US' || countryCode === 'CA' ? 'select' : 'text',
292+
options:
293+
countryCode === 'US' || countryCode === 'CA'
294+
? [
295+
{value: '', label: ''},
296+
...(countryCode === 'CA' ? provinceOptions : stateOptions)
297+
]
298+
: undefined,
296299
rules: {
297300
required:
298301
countryCode === 'CA'
299302
? 'Please select your province.' // FYI we won't translate this
300-
: formatMessage({
303+
: countryCode === 'US'
304+
? formatMessage({
301305
defaultMessage: 'Please select your state.',
302306
id: 'use_address_fields.error.please_select_your_state_or_province',
303307
description: 'Error message for a blank state (US-specific checkout)'
304308
})
309+
: false
305310
},
306311
error: errors[`${prefix}stateCode`],
307312
control
308313
},
309314
postalCode: {
310315
name: `${prefix}postalCode`,
311-
label: formatMessage(countryCode === 'CA' ? messages.postalCode : messages.zipCode),
316+
label: formatMessage(countryCode === 'US' ? messages.zipCode : messages.postalCode),
312317
defaultValue: '',
313318
type: 'text',
314319
autoComplete: 'postal-code',
315320
rules: {
316321
required:
317322
countryCode === 'CA'
318-
? 'Please enter your postal code.' // FYI we won't translate this
319-
: formatMessage({
323+
? 'Please enter your postal code.'
324+
: countryCode === 'US'
325+
? formatMessage({
320326
defaultMessage: 'Please enter your zip code.',
321327
id: 'use_address_fields.error.please_enter_your_postal_or_zip',
322328
description:
323329
'Error message for a blank zip code (US-specific checkout)'
324330
})
331+
: formatMessage({
332+
defaultMessage: 'Please enter your postal code.',
333+
id: 'use_address_fields.error.please_enter_postal_code'
334+
})
325335
},
326336
error: errors[`${prefix}postalCode`],
327337
control

0 commit comments

Comments
 (0)