@misiki/gocommerce-connector 0.31.1 → 0.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -137,7 +137,7 @@ down stays best-effort: the storefront renders from its static record.
137
137
  | You may also like | `/api/cross-sells?product_id=` — `productService.listCrossSells({ product })`, curated only; the bag's `/api/carts/{token}/cross-sells` — `cartService.listCrossSells()`, which includes the collection the shop chose for every basket (D134), falling back to the first item's related products |
138
138
  | the bag's offers and free delivery | `/api/carts/{token}/offers` — `cartService.getOffers()` and the coupon drawer's `couponService.listCoupons()`: only the codes the shop chose to show; `/api/carts/{token}/free-shipping` — `cartService.getFreeShipping()`, from the store's own rates, null when it cannot say |
139
139
  | delivery options | `/api/checkout/rates` |
140
- | payment methods | `/api/checkout` — cash on delivery, Razorpay, and the first hosted-page gateway (see below) |
140
+ | payment methods | `/api/checkout` — cash on delivery, Razorpay, Cashfree on a rupee store, and the hosted-page gateways (see below) |
141
141
  | place order | `POST /api/checkout/{code}` with an `Idempotency-Key` |
142
142
  | order lookup | `/api/orders/{number}?token=` (guest), `/x/identity/me/orders` (account) |
143
143
  | sign-in, sign-up, profile, password reset | `/x/identity/*` |
@@ -227,13 +227,14 @@ Each of these is a real gap, listed so nobody has to find it in production.
227
227
  of a request to exactly `/api/products/<slug>`; the engine's slug route is
228
228
  `/api/products/slug/<slug>`, and `/api/products/<slug>` answers 400 for a
229
229
  non-numeric id.
230
- - **Payment.** The storefront drives a fixed list of gateway flows and passes
231
- none of them the gateway the shopper chose. Cash on delivery and Razorpay's
232
- modal match engine gateways directly. The storefront's redirect flow (which it
230
+ - **Payment.** The storefront drives a fixed list of gateway flows. Cash on delivery and Razorpay's
231
+ modal match engine gateways directly, and so does Cashfree's on a rupee store,
232
+ under a neutral card icon. The storefront's redirect flow (which it
233
233
  reaches by the code `PAYPAL`) carries the store's first hosted-page gateway —
234
234
  PayPal, Adyen, Paddle, Lemon Squeezy, Creem, Hyperswitch or RevenueCat — under that
235
- gateway's own name and a neutral card icon; a second one cannot be told apart
236
- from the first and is not offered. GoCommerce's Stripe is a Payment Element (a
235
+ gateway's own name and a neutral card icon. Any further one gets its own code and
236
+ `redirects: true`, which needs `@misiki/kitcommerce-core` 0.1.60 or later to reach
237
+ its page. GoCommerce's Stripe is a Payment Element (a
237
238
  client secret), which this storefront does not mount. Every method left off is
238
239
  reported once in the console, and every one is reachable through
239
240
  `checkoutService.checkout(code)` for a storefront that drives its own payment
@@ -862,6 +862,8 @@ type CartExtras = {
862
862
  body: string;
863
863
  meta?: Record<string, string>;
864
864
  attribution?: Record<string, unknown>;
865
+ /** What placed the order, while its gateway's window can be opened again: see CheckoutService.place. */
866
+ placed?: Record<string, unknown>;
865
867
  } | null;
866
868
  };
867
869
 
@@ -4750,8 +4752,38 @@ declare class CheckoutService extends BaseService {
4750
4752
  order_no: string | null;
4751
4753
  status: string;
4752
4754
  }>;
4753
- checkoutCashfree(): Promise<never>;
4754
- captureCashfreePayment(): Promise<never>;
4755
+ /**
4756
+ * Cashfree's JavaScript checkout. The engine has already created the Cashfree
4757
+ * order; this hands the storefront the session its modal opens. `order_no` is
4758
+ * the store's number, which the storefront prefers over `order_id`, Cashfree's.
4759
+ *
4760
+ * The return URL is for the one case the modal cannot open, an in-app browser,
4761
+ * when Cashfree sends the shopper back to it instead. Only https is sent: every
4762
+ * Cashfree example is, and a plain-http dev origin should not risk the order.
4763
+ */
4764
+ checkoutCashfree({ cartId, origin }?: {
4765
+ cartId?: string;
4766
+ email?: string;
4767
+ origin?: string;
4768
+ }): Promise<{
4769
+ order_no: string;
4770
+ payment_session_id: string;
4771
+ order_id: string;
4772
+ environment: string;
4773
+ }>;
4774
+ /**
4775
+ * Nothing to capture: the signed webhook tells the engine the order is paid.
4776
+ * The storefront calls this on the server, after Cashfree's modal resolves
4777
+ * with any outcome, so it can neither claim the payment nor, without the
4778
+ * browser's order token, check it. Answering sends the shopper to the success
4779
+ * page; throwing would call a payment that may have gone through failed.
4780
+ */
4781
+ captureCashfreePayment({ order_no }?: {
4782
+ order_no?: string;
4783
+ }): Promise<{
4784
+ order_no: string | null;
4785
+ status: string;
4786
+ }>;
4755
4787
  checkoutPhonepe(): Promise<never>;
4756
4788
  createAffirmPayOrder(): Promise<never>;
4757
4789
  confirmAffirmOrder(): Promise<never>;
@@ -4896,7 +4928,7 @@ declare class CheckoutService extends BaseService {
4896
4928
  }>;
4897
4929
  /**
4898
4930
  * What checkout would charge for the cart now, by the storefront's method
4899
- * code (`COD`, `RAZORPAY`, `PAYPAL`) or none yet (D185). Null until the
4931
+ * code (`COD`, `RAZORPAY`, `CASHFREE`, `PAYPAL`) or none yet (D185). Null until the
4900
4932
  * address step has stored an address, and null from an engine before D185.
4901
4933
  *
4902
4934
  * A refusal is checkout's own: the same GoCommerceError, with the same
@@ -4943,11 +4975,17 @@ declare class CheckoutService extends BaseService {
4943
4975
  * key resumes that order, and without one the shopper is stuck. A changed
4944
4976
  * request — a different address, a different method — gets a new key, since
4945
4977
  * the engine refuses a key replayed with a different body.
4978
+ *
4979
+ * A gateway's modal keeps the key after the order is placed, along with exactly
4980
+ * what placed it. The cart is converted by then, so a shopper who closes the
4981
+ * window can pay again only under this key, and the engine then reopens the same
4982
+ * gateway order rather than risk a second charge on a new one (D232).
4946
4983
  */
4947
4984
  private place;
4948
4985
  }
4949
4986
  /** The engine's gateways that take payment on their own hosted page. */
4950
4987
  declare const REDIRECT_GATEWAYS: Set<string>;
4988
+ declare const cashfreeIsTest: () => boolean;
4951
4989
  type Expected = {
4952
4990
  total: number;
4953
4991
  onChange?: (quote: ReturnType<typeof toCheckoutQuote>, message: string) => void;
@@ -8132,4 +8170,4 @@ declare class B2bService extends BaseService {
8132
8170
  }
8133
8171
  declare const b2bService: B2bService;
8134
8172
 
8135
- export { type Faq as $, type AddOn as A, type B2bPlaced as B, type Cart as C, type Company as D, type CompanyOrder as E, ContactService as F, CountryService as G, CouponService as H, type Credentials as I, type Credit as J, CurrencyService as K, type Customer as L, type CustomizerDesign as M, type CustomizerDocument as N, type CustomizerEditor as O, type CustomizerFile as P, type CustomizerLayer as Q, type CustomizerSavedDesign as R, CustomizerService as S, type CustomizerSetup as T, type CustomizerUpload as U, DealService as V, DemoRequestService as W, type EngineRequest as X, EnquiryService as Y, type Envelope as Z, type FacetValue as _, type Address as a, type SiteChrome as a$, FaqService as a0, FeedbackService as a1, type FooterSettings as a2, type FreeShipping as a3, type Fulfillment as a4, GalleryService as a5, type GooglePlace as a6, type GoogleReview as a7, GoogleReviewService as a8, type GoogleReviews as a9, type PageLayout as aA, PageService as aB, type PaymentIntent as aC, PaymentMethodService as aD, type Plugin as aE, PluginService as aF, PopularSearchService as aG, PopularityService as aH, type Postcode as aI, type Product as aJ, type ProductFacets as aK, type ProductOption as aL, ProductService as aM, ProfileService as aN, type Quote as aO, REDIRECT_GATEWAYS as aP, ReelsService as aQ, RegionService as aR, type RejectedLine as aS, type Review as aT, type ReviewPage as aU, ReviewService as aV, type SearchDoc as aW, type SearchResult as aX, SearchService as aY, SettingService as aZ, type ShippingRate as a_, type GoogleSurvey as aa, type HeaderSettings as ab, type HomeLayout as ac, type HomeLayoutSection as ad, HomeService as ae, type IdentityAddress as af, InitService as ag, type Invitation as ah, type LayoutCollection as ai, type LayoutPage as aj, LayoutService as ak, type Lead as al, type ListMeta as am, MeilisearchService as an, type Member as ao, type Menu as ap, type MenuItem as aq, MenuService as ar, type MenuSummary as as, type Money as at, NewsletterService as au, type Offer as av, type OpeningHours as aw, type Order as ax, type OrderLine as ay, OrderService as az, AddressService as b, layoutService as b$, type SiteFooter as b0, type SiteHeader as b1, type SocialLink as b2, StateService as b3, type StoreClosed as b4, type StorePass as b5, type StorePassAnswer as b6, type StoreProfile as b7, StoreService as b8, type StoreUnlock as b9, bannerService as bA, blogService as bB, cartService as bC, categoryService as bD, chatService as bE, checkoutService as bF, collectionService as bG, contactService as bH, countryService as bI, couponService as bJ, currencyExponent as bK, currencyService as bL, customizerService as bM, dealService as bN, demoRequestService as bO, emptyCart as bP, emptyPage as bQ, enquiryService as bR, faqService as bS, feedbackService as bT, fromEngineAddress as bU, fromIdentityAddress as bV, fromSearchDoc as bW, galleryService as bX, googleReviewService as bY, homeService as bZ, initService as b_, type StorefrontCategory as ba, type StorefrontMenu as bb, type StorefrontOrder as bc, type StorefrontProduct as bd, type ThemeSettings as be, type ThemeSettingsResult as bf, UploadService as bg, UserService as bh, type Variant as bi, type VariantImage as bj, VariantService as bk, VarniCustomDesignService as bl, VarniCustomProductService as bm, type Vendor as bn, VendorService as bo, WarrantyService as bp, type WhatsappConsent as bq, type WhatsappConsentOffer as br, WhatsappService as bs, type Wishlist as bt, type WishlistItem as bu, WishlistService as bv, addressService as bw, authService as bx, autocompleteService as by, b2bService as bz, type AppliedDiscount as c, mediaUrl as c0, meilisearchService as c1, menuService as c2, minorToMajor as c3, newsletterService as c4, orderService as c5, ownDomain as c6, pageService as c7, paymentMethodService as c8, pluginService as c9, toCmsPage as cA, toCollection as cB, toEngineAddress as cC, toFreeShipping as cD, toIdentityAddress as cE, toMajor as cF, toMinor as cG, toOrder as cH, toPage as cI, toProduct as cJ, toRating as cK, toStorefrontFacets as cL, toUser as cM, toVariant as cN, toVendor as cO, uploadService as cP, userService as cQ, variantService as cR, varniCustomDesignService as cS, varniCustomProductService as cT, vendorService as cU, warrantyService as cV, whatsappService as cW, wishlistService as cX, pluginsService as ca, popularSearchService as cb, popularityService as cc, priceOf as cd, productImages as ce, productService as cf, productVideos as cg, profileService as ch, qs as ci, reelsService as cj, regionService as ck, reviewService as cl, searchService as cm, setRequestHost as cn, settingService as co, settingsService as cp, stateService as cq, staticStore as cr, storeForHost as cs, storeService as ct, toAddOn as cu, toCart as cv, toCartLine as cw, toCartOffer as cx, toCategory as cy, toCheckoutQuote as cz, type Approval as d, type AuthResponse as e, AuthService as f, AutocompleteService as g, type B2bRole as h, B2bService as i, BannerService as j, BaseService as k, BlogService as l, type CartLine as m, type CartOffer as n, CartService as o, CatalogService as p, type Category as q, CategoryService as r, ChatService as s, type CheckoutOptions as t, type CheckoutQuote as u, type CheckoutResult as v, CheckoutService as w, type CmsPage as x, type Collection as y, CollectionService as z };
8173
+ export { type Faq as $, type AddOn as A, type B2bPlaced as B, type Cart as C, type Company as D, type CompanyOrder as E, ContactService as F, CountryService as G, CouponService as H, type Credentials as I, type Credit as J, CurrencyService as K, type Customer as L, type CustomizerDesign as M, type CustomizerDocument as N, type CustomizerEditor as O, type CustomizerFile as P, type CustomizerLayer as Q, type CustomizerSavedDesign as R, CustomizerService as S, type CustomizerSetup as T, type CustomizerUpload as U, DealService as V, DemoRequestService as W, type EngineRequest as X, EnquiryService as Y, type Envelope as Z, type FacetValue as _, type Address as a, type SiteChrome as a$, FaqService as a0, FeedbackService as a1, type FooterSettings as a2, type FreeShipping as a3, type Fulfillment as a4, GalleryService as a5, type GooglePlace as a6, type GoogleReview as a7, GoogleReviewService as a8, type GoogleReviews as a9, type PageLayout as aA, PageService as aB, type PaymentIntent as aC, PaymentMethodService as aD, type Plugin as aE, PluginService as aF, PopularSearchService as aG, PopularityService as aH, type Postcode as aI, type Product as aJ, type ProductFacets as aK, type ProductOption as aL, ProductService as aM, ProfileService as aN, type Quote as aO, REDIRECT_GATEWAYS as aP, ReelsService as aQ, RegionService as aR, type RejectedLine as aS, type Review as aT, type ReviewPage as aU, ReviewService as aV, type SearchDoc as aW, type SearchResult as aX, SearchService as aY, SettingService as aZ, type ShippingRate as a_, type GoogleSurvey as aa, type HeaderSettings as ab, type HomeLayout as ac, type HomeLayoutSection as ad, HomeService as ae, type IdentityAddress as af, InitService as ag, type Invitation as ah, type LayoutCollection as ai, type LayoutPage as aj, LayoutService as ak, type Lead as al, type ListMeta as am, MeilisearchService as an, type Member as ao, type Menu as ap, type MenuItem as aq, MenuService as ar, type MenuSummary as as, type Money as at, NewsletterService as au, type Offer as av, type OpeningHours as aw, type Order as ax, type OrderLine as ay, OrderService as az, AddressService as b, initService as b$, type SiteFooter as b0, type SiteHeader as b1, type SocialLink as b2, StateService as b3, type StoreClosed as b4, type StorePass as b5, type StorePassAnswer as b6, type StoreProfile as b7, StoreService as b8, type StoreUnlock as b9, bannerService as bA, blogService as bB, cartService as bC, cashfreeIsTest as bD, categoryService as bE, chatService as bF, checkoutService as bG, collectionService as bH, contactService as bI, countryService as bJ, couponService as bK, currencyExponent as bL, currencyService as bM, customizerService as bN, dealService as bO, demoRequestService as bP, emptyCart as bQ, emptyPage as bR, enquiryService as bS, faqService as bT, feedbackService as bU, fromEngineAddress as bV, fromIdentityAddress as bW, fromSearchDoc as bX, galleryService as bY, googleReviewService as bZ, homeService as b_, type StorefrontCategory as ba, type StorefrontMenu as bb, type StorefrontOrder as bc, type StorefrontProduct as bd, type ThemeSettings as be, type ThemeSettingsResult as bf, UploadService as bg, UserService as bh, type Variant as bi, type VariantImage as bj, VariantService as bk, VarniCustomDesignService as bl, VarniCustomProductService as bm, type Vendor as bn, VendorService as bo, WarrantyService as bp, type WhatsappConsent as bq, type WhatsappConsentOffer as br, WhatsappService as bs, type Wishlist as bt, type WishlistItem as bu, WishlistService as bv, addressService as bw, authService as bx, autocompleteService as by, b2bService as bz, type AppliedDiscount as c, layoutService as c0, mediaUrl as c1, meilisearchService as c2, menuService as c3, minorToMajor as c4, newsletterService as c5, orderService as c6, ownDomain as c7, pageService as c8, paymentMethodService as c9, toCheckoutQuote as cA, toCmsPage as cB, toCollection as cC, toEngineAddress as cD, toFreeShipping as cE, toIdentityAddress as cF, toMajor as cG, toMinor as cH, toOrder as cI, toPage as cJ, toProduct as cK, toRating as cL, toStorefrontFacets as cM, toUser as cN, toVariant as cO, toVendor as cP, uploadService as cQ, userService as cR, variantService as cS, varniCustomDesignService as cT, varniCustomProductService as cU, vendorService as cV, warrantyService as cW, whatsappService as cX, wishlistService as cY, pluginService as ca, pluginsService as cb, popularSearchService as cc, popularityService as cd, priceOf as ce, productImages as cf, productService as cg, productVideos as ch, profileService as ci, qs as cj, reelsService as ck, regionService as cl, reviewService as cm, searchService as cn, setRequestHost as co, settingService as cp, settingsService as cq, stateService as cr, staticStore as cs, storeForHost as ct, storeService as cu, toAddOn as cv, toCart as cw, toCartLine as cx, toCartOffer as cy, toCategory as cz, type Approval as d, type AuthResponse as e, AuthService as f, AutocompleteService as g, type B2bRole as h, B2bService as i, BannerService as j, BaseService as k, BlogService as l, type CartLine as m, type CartOffer as n, CartService as o, CatalogService as p, type Category as q, CategoryService as r, ChatService as s, type CheckoutOptions as t, type CheckoutQuote as u, type CheckoutResult as v, CheckoutService as w, type CmsPage as x, type Collection as y, CollectionService as z };
@@ -862,6 +862,8 @@ type CartExtras = {
862
862
  body: string;
863
863
  meta?: Record<string, string>;
864
864
  attribution?: Record<string, unknown>;
865
+ /** What placed the order, while its gateway's window can be opened again: see CheckoutService.place. */
866
+ placed?: Record<string, unknown>;
865
867
  } | null;
866
868
  };
867
869
 
@@ -4750,8 +4752,38 @@ declare class CheckoutService extends BaseService {
4750
4752
  order_no: string | null;
4751
4753
  status: string;
4752
4754
  }>;
4753
- checkoutCashfree(): Promise<never>;
4754
- captureCashfreePayment(): Promise<never>;
4755
+ /**
4756
+ * Cashfree's JavaScript checkout. The engine has already created the Cashfree
4757
+ * order; this hands the storefront the session its modal opens. `order_no` is
4758
+ * the store's number, which the storefront prefers over `order_id`, Cashfree's.
4759
+ *
4760
+ * The return URL is for the one case the modal cannot open, an in-app browser,
4761
+ * when Cashfree sends the shopper back to it instead. Only https is sent: every
4762
+ * Cashfree example is, and a plain-http dev origin should not risk the order.
4763
+ */
4764
+ checkoutCashfree({ cartId, origin }?: {
4765
+ cartId?: string;
4766
+ email?: string;
4767
+ origin?: string;
4768
+ }): Promise<{
4769
+ order_no: string;
4770
+ payment_session_id: string;
4771
+ order_id: string;
4772
+ environment: string;
4773
+ }>;
4774
+ /**
4775
+ * Nothing to capture: the signed webhook tells the engine the order is paid.
4776
+ * The storefront calls this on the server, after Cashfree's modal resolves
4777
+ * with any outcome, so it can neither claim the payment nor, without the
4778
+ * browser's order token, check it. Answering sends the shopper to the success
4779
+ * page; throwing would call a payment that may have gone through failed.
4780
+ */
4781
+ captureCashfreePayment({ order_no }?: {
4782
+ order_no?: string;
4783
+ }): Promise<{
4784
+ order_no: string | null;
4785
+ status: string;
4786
+ }>;
4755
4787
  checkoutPhonepe(): Promise<never>;
4756
4788
  createAffirmPayOrder(): Promise<never>;
4757
4789
  confirmAffirmOrder(): Promise<never>;
@@ -4896,7 +4928,7 @@ declare class CheckoutService extends BaseService {
4896
4928
  }>;
4897
4929
  /**
4898
4930
  * What checkout would charge for the cart now, by the storefront's method
4899
- * code (`COD`, `RAZORPAY`, `PAYPAL`) or none yet (D185). Null until the
4931
+ * code (`COD`, `RAZORPAY`, `CASHFREE`, `PAYPAL`) or none yet (D185). Null until the
4900
4932
  * address step has stored an address, and null from an engine before D185.
4901
4933
  *
4902
4934
  * A refusal is checkout's own: the same GoCommerceError, with the same
@@ -4943,11 +4975,17 @@ declare class CheckoutService extends BaseService {
4943
4975
  * key resumes that order, and without one the shopper is stuck. A changed
4944
4976
  * request — a different address, a different method — gets a new key, since
4945
4977
  * the engine refuses a key replayed with a different body.
4978
+ *
4979
+ * A gateway's modal keeps the key after the order is placed, along with exactly
4980
+ * what placed it. The cart is converted by then, so a shopper who closes the
4981
+ * window can pay again only under this key, and the engine then reopens the same
4982
+ * gateway order rather than risk a second charge on a new one (D232).
4946
4983
  */
4947
4984
  private place;
4948
4985
  }
4949
4986
  /** The engine's gateways that take payment on their own hosted page. */
4950
4987
  declare const REDIRECT_GATEWAYS: Set<string>;
4988
+ declare const cashfreeIsTest: () => boolean;
4951
4989
  type Expected = {
4952
4990
  total: number;
4953
4991
  onChange?: (quote: ReturnType<typeof toCheckoutQuote>, message: string) => void;
@@ -8132,4 +8170,4 @@ declare class B2bService extends BaseService {
8132
8170
  }
8133
8171
  declare const b2bService: B2bService;
8134
8172
 
8135
- export { type Faq as $, type AddOn as A, type B2bPlaced as B, type Cart as C, type Company as D, type CompanyOrder as E, ContactService as F, CountryService as G, CouponService as H, type Credentials as I, type Credit as J, CurrencyService as K, type Customer as L, type CustomizerDesign as M, type CustomizerDocument as N, type CustomizerEditor as O, type CustomizerFile as P, type CustomizerLayer as Q, type CustomizerSavedDesign as R, CustomizerService as S, type CustomizerSetup as T, type CustomizerUpload as U, DealService as V, DemoRequestService as W, type EngineRequest as X, EnquiryService as Y, type Envelope as Z, type FacetValue as _, type Address as a, type SiteChrome as a$, FaqService as a0, FeedbackService as a1, type FooterSettings as a2, type FreeShipping as a3, type Fulfillment as a4, GalleryService as a5, type GooglePlace as a6, type GoogleReview as a7, GoogleReviewService as a8, type GoogleReviews as a9, type PageLayout as aA, PageService as aB, type PaymentIntent as aC, PaymentMethodService as aD, type Plugin as aE, PluginService as aF, PopularSearchService as aG, PopularityService as aH, type Postcode as aI, type Product as aJ, type ProductFacets as aK, type ProductOption as aL, ProductService as aM, ProfileService as aN, type Quote as aO, REDIRECT_GATEWAYS as aP, ReelsService as aQ, RegionService as aR, type RejectedLine as aS, type Review as aT, type ReviewPage as aU, ReviewService as aV, type SearchDoc as aW, type SearchResult as aX, SearchService as aY, SettingService as aZ, type ShippingRate as a_, type GoogleSurvey as aa, type HeaderSettings as ab, type HomeLayout as ac, type HomeLayoutSection as ad, HomeService as ae, type IdentityAddress as af, InitService as ag, type Invitation as ah, type LayoutCollection as ai, type LayoutPage as aj, LayoutService as ak, type Lead as al, type ListMeta as am, MeilisearchService as an, type Member as ao, type Menu as ap, type MenuItem as aq, MenuService as ar, type MenuSummary as as, type Money as at, NewsletterService as au, type Offer as av, type OpeningHours as aw, type Order as ax, type OrderLine as ay, OrderService as az, AddressService as b, layoutService as b$, type SiteFooter as b0, type SiteHeader as b1, type SocialLink as b2, StateService as b3, type StoreClosed as b4, type StorePass as b5, type StorePassAnswer as b6, type StoreProfile as b7, StoreService as b8, type StoreUnlock as b9, bannerService as bA, blogService as bB, cartService as bC, categoryService as bD, chatService as bE, checkoutService as bF, collectionService as bG, contactService as bH, countryService as bI, couponService as bJ, currencyExponent as bK, currencyService as bL, customizerService as bM, dealService as bN, demoRequestService as bO, emptyCart as bP, emptyPage as bQ, enquiryService as bR, faqService as bS, feedbackService as bT, fromEngineAddress as bU, fromIdentityAddress as bV, fromSearchDoc as bW, galleryService as bX, googleReviewService as bY, homeService as bZ, initService as b_, type StorefrontCategory as ba, type StorefrontMenu as bb, type StorefrontOrder as bc, type StorefrontProduct as bd, type ThemeSettings as be, type ThemeSettingsResult as bf, UploadService as bg, UserService as bh, type Variant as bi, type VariantImage as bj, VariantService as bk, VarniCustomDesignService as bl, VarniCustomProductService as bm, type Vendor as bn, VendorService as bo, WarrantyService as bp, type WhatsappConsent as bq, type WhatsappConsentOffer as br, WhatsappService as bs, type Wishlist as bt, type WishlistItem as bu, WishlistService as bv, addressService as bw, authService as bx, autocompleteService as by, b2bService as bz, type AppliedDiscount as c, mediaUrl as c0, meilisearchService as c1, menuService as c2, minorToMajor as c3, newsletterService as c4, orderService as c5, ownDomain as c6, pageService as c7, paymentMethodService as c8, pluginService as c9, toCmsPage as cA, toCollection as cB, toEngineAddress as cC, toFreeShipping as cD, toIdentityAddress as cE, toMajor as cF, toMinor as cG, toOrder as cH, toPage as cI, toProduct as cJ, toRating as cK, toStorefrontFacets as cL, toUser as cM, toVariant as cN, toVendor as cO, uploadService as cP, userService as cQ, variantService as cR, varniCustomDesignService as cS, varniCustomProductService as cT, vendorService as cU, warrantyService as cV, whatsappService as cW, wishlistService as cX, pluginsService as ca, popularSearchService as cb, popularityService as cc, priceOf as cd, productImages as ce, productService as cf, productVideos as cg, profileService as ch, qs as ci, reelsService as cj, regionService as ck, reviewService as cl, searchService as cm, setRequestHost as cn, settingService as co, settingsService as cp, stateService as cq, staticStore as cr, storeForHost as cs, storeService as ct, toAddOn as cu, toCart as cv, toCartLine as cw, toCartOffer as cx, toCategory as cy, toCheckoutQuote as cz, type Approval as d, type AuthResponse as e, AuthService as f, AutocompleteService as g, type B2bRole as h, B2bService as i, BannerService as j, BaseService as k, BlogService as l, type CartLine as m, type CartOffer as n, CartService as o, CatalogService as p, type Category as q, CategoryService as r, ChatService as s, type CheckoutOptions as t, type CheckoutQuote as u, type CheckoutResult as v, CheckoutService as w, type CmsPage as x, type Collection as y, CollectionService as z };
8173
+ export { type Faq as $, type AddOn as A, type B2bPlaced as B, type Cart as C, type Company as D, type CompanyOrder as E, ContactService as F, CountryService as G, CouponService as H, type Credentials as I, type Credit as J, CurrencyService as K, type Customer as L, type CustomizerDesign as M, type CustomizerDocument as N, type CustomizerEditor as O, type CustomizerFile as P, type CustomizerLayer as Q, type CustomizerSavedDesign as R, CustomizerService as S, type CustomizerSetup as T, type CustomizerUpload as U, DealService as V, DemoRequestService as W, type EngineRequest as X, EnquiryService as Y, type Envelope as Z, type FacetValue as _, type Address as a, type SiteChrome as a$, FaqService as a0, FeedbackService as a1, type FooterSettings as a2, type FreeShipping as a3, type Fulfillment as a4, GalleryService as a5, type GooglePlace as a6, type GoogleReview as a7, GoogleReviewService as a8, type GoogleReviews as a9, type PageLayout as aA, PageService as aB, type PaymentIntent as aC, PaymentMethodService as aD, type Plugin as aE, PluginService as aF, PopularSearchService as aG, PopularityService as aH, type Postcode as aI, type Product as aJ, type ProductFacets as aK, type ProductOption as aL, ProductService as aM, ProfileService as aN, type Quote as aO, REDIRECT_GATEWAYS as aP, ReelsService as aQ, RegionService as aR, type RejectedLine as aS, type Review as aT, type ReviewPage as aU, ReviewService as aV, type SearchDoc as aW, type SearchResult as aX, SearchService as aY, SettingService as aZ, type ShippingRate as a_, type GoogleSurvey as aa, type HeaderSettings as ab, type HomeLayout as ac, type HomeLayoutSection as ad, HomeService as ae, type IdentityAddress as af, InitService as ag, type Invitation as ah, type LayoutCollection as ai, type LayoutPage as aj, LayoutService as ak, type Lead as al, type ListMeta as am, MeilisearchService as an, type Member as ao, type Menu as ap, type MenuItem as aq, MenuService as ar, type MenuSummary as as, type Money as at, NewsletterService as au, type Offer as av, type OpeningHours as aw, type Order as ax, type OrderLine as ay, OrderService as az, AddressService as b, initService as b$, type SiteFooter as b0, type SiteHeader as b1, type SocialLink as b2, StateService as b3, type StoreClosed as b4, type StorePass as b5, type StorePassAnswer as b6, type StoreProfile as b7, StoreService as b8, type StoreUnlock as b9, bannerService as bA, blogService as bB, cartService as bC, cashfreeIsTest as bD, categoryService as bE, chatService as bF, checkoutService as bG, collectionService as bH, contactService as bI, countryService as bJ, couponService as bK, currencyExponent as bL, currencyService as bM, customizerService as bN, dealService as bO, demoRequestService as bP, emptyCart as bQ, emptyPage as bR, enquiryService as bS, faqService as bT, feedbackService as bU, fromEngineAddress as bV, fromIdentityAddress as bW, fromSearchDoc as bX, galleryService as bY, googleReviewService as bZ, homeService as b_, type StorefrontCategory as ba, type StorefrontMenu as bb, type StorefrontOrder as bc, type StorefrontProduct as bd, type ThemeSettings as be, type ThemeSettingsResult as bf, UploadService as bg, UserService as bh, type Variant as bi, type VariantImage as bj, VariantService as bk, VarniCustomDesignService as bl, VarniCustomProductService as bm, type Vendor as bn, VendorService as bo, WarrantyService as bp, type WhatsappConsent as bq, type WhatsappConsentOffer as br, WhatsappService as bs, type Wishlist as bt, type WishlistItem as bu, WishlistService as bv, addressService as bw, authService as bx, autocompleteService as by, b2bService as bz, type AppliedDiscount as c, layoutService as c0, mediaUrl as c1, meilisearchService as c2, menuService as c3, minorToMajor as c4, newsletterService as c5, orderService as c6, ownDomain as c7, pageService as c8, paymentMethodService as c9, toCheckoutQuote as cA, toCmsPage as cB, toCollection as cC, toEngineAddress as cD, toFreeShipping as cE, toIdentityAddress as cF, toMajor as cG, toMinor as cH, toOrder as cI, toPage as cJ, toProduct as cK, toRating as cL, toStorefrontFacets as cM, toUser as cN, toVariant as cO, toVendor as cP, uploadService as cQ, userService as cR, variantService as cS, varniCustomDesignService as cT, varniCustomProductService as cU, vendorService as cV, warrantyService as cW, whatsappService as cX, wishlistService as cY, pluginService as ca, pluginsService as cb, popularSearchService as cc, popularityService as cd, priceOf as ce, productImages as cf, productService as cg, productVideos as ch, profileService as ci, qs as cj, reelsService as ck, regionService as cl, reviewService as cm, searchService as cn, setRequestHost as co, settingService as cp, settingsService as cq, stateService as cr, staticStore as cs, storeForHost as ct, storeService as cu, toAddOn as cv, toCart as cw, toCartLine as cx, toCartOffer as cy, toCategory as cz, type Approval as d, type AuthResponse as e, AuthService as f, AutocompleteService as g, type B2bRole as h, B2bService as i, BannerService as j, BaseService as k, BlogService as l, type CartLine as m, type CartOffer as n, CartService as o, CatalogService as p, type Category as q, CategoryService as r, ChatService as s, type CheckoutOptions as t, type CheckoutQuote as u, type CheckoutResult as v, CheckoutService as w, type CmsPage as x, type Collection as y, CollectionService as z };
package/dist/index.cjs CHANGED
@@ -2216,9 +2216,9 @@ var CheckoutService = class extends BaseService {
2216
2216
  }
2217
2217
  // The family's other gateway flows. GoCommerce's Stripe is a Payment
2218
2218
  // Element (a client secret, not a hosted page) — `checkout('stripe')` returns
2219
- // that secret for a storefront that mounts the element. Cashfree, PhonePe and
2220
- // Affirm are not engine gateways at all. paymentMethodService never offers
2221
- // these codes, so they are reached only by a storefront that hard-codes one.
2219
+ // that secret for a storefront that mounts the element. PhonePe and Affirm are
2220
+ // not engine gateways at all. paymentMethodService never offers these codes,
2221
+ // so they are reached only by a storefront that hard-codes one.
2222
2222
  async checkoutStripe() {
2223
2223
  throw unsupported(
2224
2224
  "checkoutStripe",
@@ -2241,7 +2241,7 @@ var CheckoutService = class extends BaseService {
2241
2241
  const gateway = await this.redirectGateway();
2242
2242
  if (!gateway) throw unsupported("checkoutPaypal", "this store has no hosted-page payment gateway switched on");
2243
2243
  const id2 = cartId || storedCartId() || "";
2244
- const back = origin ? `${origin.replace(/\/+$/, "")}/checkout/success?cart_id=${encodeURIComponent(id2)}` : void 0;
2244
+ const back = origin ? successUrl(origin, id2) : void 0;
2245
2245
  const result = await this.place(gateway, id2, back ? { return_url: back } : {});
2246
2246
  const url = result.payment.client_data?.url;
2247
2247
  if (!url) throw new GoCommerceError("The payment page did not open. Please try again.", 502);
@@ -2257,11 +2257,40 @@ var CheckoutService = class extends BaseService {
2257
2257
  } = {}) {
2258
2258
  return { order_no: order_no ?? null, status: "processing" };
2259
2259
  }
2260
- async checkoutCashfree() {
2261
- throw unsupported("checkoutCashfree", "the engine has no Cashfree gateway");
2260
+ /**
2261
+ * Cashfree's JavaScript checkout. The engine has already created the Cashfree
2262
+ * order; this hands the storefront the session its modal opens. `order_no` is
2263
+ * the store's number, which the storefront prefers over `order_id`, Cashfree's.
2264
+ *
2265
+ * The return URL is for the one case the modal cannot open, an in-app browser,
2266
+ * when Cashfree sends the shopper back to it instead. Only https is sent: every
2267
+ * Cashfree example is, and a plain-http dev origin should not risk the order.
2268
+ */
2269
+ async checkoutCashfree({ cartId, origin } = {}) {
2270
+ const { id: id2, extras, address } = await this.storedCheckout(cartId);
2271
+ if (!extras.phone && !address.phone) throw new GoCommerceError("Add a phone number to pay with Cashfree.", 400);
2272
+ const result = await this.place("cashfree", id2, origin?.startsWith("https:") ? { return_url: successUrl(origin, id2) } : {});
2273
+ const data = result.payment.client_data ?? {};
2274
+ if (result.payment.kind !== "client_action" || !data.payment_session_id) {
2275
+ throw new GoCommerceError("The payment page did not open. Please try again.", 502);
2276
+ }
2277
+ cashfreeEnvironment = data.environment;
2278
+ return {
2279
+ order_no: result.order.number,
2280
+ payment_session_id: data.payment_session_id,
2281
+ order_id: data.order_id,
2282
+ environment: data.environment
2283
+ };
2262
2284
  }
2263
- async captureCashfreePayment() {
2264
- throw unsupported("captureCashfreePayment", "the engine has no Cashfree gateway");
2285
+ /**
2286
+ * Nothing to capture: the signed webhook tells the engine the order is paid.
2287
+ * The storefront calls this on the server, after Cashfree's modal resolves
2288
+ * with any outcome, so it can neither claim the payment nor, without the
2289
+ * browser's order token, check it. Answering sends the shopper to the success
2290
+ * page; throwing would call a payment that may have gone through failed.
2291
+ */
2292
+ async captureCashfreePayment({ order_no } = {}) {
2293
+ return { order_no: order_no ?? null, status: "processing" };
2265
2294
  }
2266
2295
  async checkoutPhonepe() {
2267
2296
  throw unsupported("checkoutPhonepe", "the engine has no PhonePe gateway");
@@ -2327,7 +2356,7 @@ var CheckoutService = class extends BaseService {
2327
2356
  }
2328
2357
  /**
2329
2358
  * What checkout would charge for the cart now, by the storefront's method
2330
- * code (`COD`, `RAZORPAY`, `PAYPAL`) or none yet (D185). Null until the
2359
+ * code (`COD`, `RAZORPAY`, `CASHFREE`, `PAYPAL`) or none yet (D185). Null until the
2331
2360
  * address step has stored an address, and null from an engine before D185.
2332
2361
  *
2333
2362
  * A refusal is checkout's own: the same GoCommerceError, with the same
@@ -2335,7 +2364,7 @@ var CheckoutService = class extends BaseService {
2335
2364
  */
2336
2365
  async getQuote({ cartId, method = "" } = {}) {
2337
2366
  const id2 = cartId || storedCartId();
2338
- if (!id2 || !cartExtras(id2).shippingAddress) return null;
2367
+ if (!id2 || !cartExtras(id2).shippingAddress || cartExtras(id2).attempt?.placed) return null;
2339
2368
  const { extras, email, address } = await this.storedCheckout(id2);
2340
2369
  const code = method.toLowerCase() === "paypal" ? await this.redirectGateway() : method.toLowerCase();
2341
2370
  try {
@@ -2396,6 +2425,11 @@ var CheckoutService = class extends BaseService {
2396
2425
  * key resumes that order, and without one the shopper is stuck. A changed
2397
2426
  * request — a different address, a different method — gets a new key, since
2398
2427
  * the engine refuses a key replayed with a different body.
2428
+ *
2429
+ * A gateway's modal keeps the key after the order is placed, along with exactly
2430
+ * what placed it. The cart is converted by then, so a shopper who closes the
2431
+ * window can pay again only under this key, and the engine then reopens the same
2432
+ * gateway order rather than risk a second charge on a new one (D232).
2399
2433
  */
2400
2434
  async place(code, cartId, extra = {}) {
2401
2435
  const { id: id2, extras, email, address, currency } = await this.storedCheckout(cartId);
@@ -2412,23 +2446,27 @@ var CheckoutService = class extends BaseService {
2412
2446
  const serialised = JSON.stringify(body);
2413
2447
  const retry = extras.attempt?.body === `${code}:${serialised}`;
2414
2448
  const key = retry ? extras.attempt.key : newKey();
2449
+ const placed = retry ? extras.attempt?.placed : void 0;
2415
2450
  const meta = retry ? extras.attempt?.meta : metaBrowser();
2416
2451
  const attribution = retry ? extras.attempt?.attribution : landingAttribution();
2417
- saveCartExtras(id2, {
2418
- attempt: { key, body: `${code}:${serialised}`, ...meta ? { meta } : {}, ...attribution ? { attribution } : {} }
2419
- });
2452
+ if (!placed) {
2453
+ saveCartExtras(id2, {
2454
+ attempt: { key, body: `${code}:${serialised}`, ...meta ? { meta } : {}, ...attribution ? { attribution } : {} }
2455
+ });
2456
+ }
2420
2457
  const metadata = {
2421
2458
  ...extra.metadata,
2422
2459
  ...meta ? { meta_capi: meta } : {},
2423
2460
  ...attribution ? { attribution } : {}
2424
2461
  };
2425
2462
  const expected = this.expected.get(id2);
2463
+ const sent = placed ?? {
2464
+ ...Object.keys(metadata).length ? { ...body, metadata } : body,
2465
+ ...expected ? { quoted_total_minor: toMinor(expected.total, currency) } : {}
2466
+ };
2426
2467
  const result = await this.data(`/api/checkout/${encodeURIComponent(code)}`, {
2427
2468
  method: "POST",
2428
- body: {
2429
- ...Object.keys(metadata).length ? { ...body, metadata } : body,
2430
- ...expected ? { quoted_total_minor: toMinor(expected.total, currency) } : {}
2431
- },
2469
+ body: sent,
2432
2470
  headers: { "Idempotency-Key": key }
2433
2471
  }).catch((err) => {
2434
2472
  if (keyAlreadyUsed(err)) {
@@ -2455,11 +2493,18 @@ var CheckoutService = class extends BaseService {
2455
2493
  }).catch(() => void 0);
2456
2494
  }
2457
2495
  }
2458
- saveCartExtras(id2, { attempt: null });
2496
+ const reopenable = result.payment.kind === "client_action";
2497
+ saveCartExtras(id2, { attempt: reopenable ? { key, body: `${code}:${serialised}`, placed: sent } : null });
2498
+ if (reopenable && result.order.payment_status === "paid") {
2499
+ throw new GoCommerceError(`Order ${result.order.number} is already paid, so there is nothing more to pay.`, 409, "already_paid");
2500
+ }
2459
2501
  return result;
2460
2502
  }
2461
2503
  };
2462
2504
  var REDIRECT_GATEWAYS = /* @__PURE__ */ new Set(["paypal", "adyen", "paddle", "lemonsqueezy", "creem", "hyperswitch", "revenuecat"]);
2505
+ var cashfreeEnvironment;
2506
+ var cashfreeIsTest = () => cashfreeEnvironment === "sandbox";
2507
+ var successUrl = (origin, cartId) => `${origin.replace(/\/+$/, "")}/checkout/success?cart_id=${encodeURIComponent(cartId)}`;
2463
2508
  var unsupported = (method, why) => new NotSupportedError("CheckoutService", method, why);
2464
2509
  function keyAlreadyUsed(err) {
2465
2510
  return err instanceof GoCommerceError && err.status === 400 && err.message.includes("Idempotency-Key was already used");
@@ -2486,7 +2531,8 @@ var checkoutService = new CheckoutService();
2486
2531
  // src/services/payment-method-service.ts
2487
2532
  var DRIVABLE = {
2488
2533
  cod: { code: "COD", img: "/payment/cod.svg" },
2489
- razorpay: { code: "RAZORPAY" }
2534
+ razorpay: { code: "RAZORPAY" },
2535
+ cashfree: { code: "CASHFREE", img: "/payment/card.svg" }
2490
2536
  };
2491
2537
  var reported2 = /* @__PURE__ */ new Set();
2492
2538
  var PaymentMethodService = class extends BaseService {
@@ -2495,7 +2541,12 @@ var PaymentMethodService = class extends BaseService {
2495
2541
  const options = await this.facts.checkoutOptions();
2496
2542
  const methods = options.methods ?? [];
2497
2543
  const redirect = methods.find((m) => REDIRECT_GATEWAYS.has(m.code))?.code;
2498
- const drive = (code) => DRIVABLE[code] ?? (code === redirect ? { code: "PAYPAL", img: "/payment/card.svg" } : void 0);
2544
+ const drive = (code) => {
2545
+ if (code === "cashfree" && options.currency !== "INR") return void 0;
2546
+ if (DRIVABLE[code]) return DRIVABLE[code];
2547
+ if (code === redirect) return { code: "PAYPAL", img: "/payment/card.svg" };
2548
+ return REDIRECT_GATEWAYS.has(code) ? { code: code.toUpperCase(), img: "/payment/card.svg" } : void 0;
2549
+ };
2499
2550
  const data = methods.filter((m) => {
2500
2551
  if (drive(m.code)) return true;
2501
2552
  if (!reported2.has(m.code)) {
@@ -2505,19 +2556,24 @@ var PaymentMethodService = class extends BaseService {
2505
2556
  );
2506
2557
  }
2507
2558
  return false;
2508
- }).map((m, i) => ({
2509
- id: m.code,
2510
- code: drive(m.code).code,
2511
- engineCode: m.code,
2512
- name: m.name,
2513
- title: m.name,
2514
- description: "",
2515
- img: drive(m.code).img ?? null,
2516
- fee: toMajor(m.fee),
2517
- rank: i,
2518
- active: true,
2519
- isActive: true
2520
- }));
2559
+ }).map((m, i) => {
2560
+ const method = {
2561
+ id: m.code,
2562
+ code: drive(m.code).code,
2563
+ engineCode: m.code,
2564
+ name: m.name,
2565
+ title: m.name,
2566
+ description: "",
2567
+ img: drive(m.code).img ?? null,
2568
+ fee: toMajor(m.fee),
2569
+ rank: i,
2570
+ active: true,
2571
+ isActive: true
2572
+ };
2573
+ if (REDIRECT_GATEWAYS.has(m.code) && m.code !== redirect) Object.assign(method, { redirects: true });
2574
+ if (m.code === "cashfree") Object.defineProperty(method, "isTest", { get: cashfreeIsTest, enumerable: true });
2575
+ return method;
2576
+ });
2521
2577
  return { data, count: data.length, pageSize: data.length, noOfPage: 1, page: 1 };
2522
2578
  }
2523
2579
  };
@@ -4345,6 +4401,7 @@ exports.b2bService = b2bService;
4345
4401
  exports.bannerService = bannerService;
4346
4402
  exports.blogService = blogService;
4347
4403
  exports.cartService = cartService;
4404
+ exports.cashfreeIsTest = cashfreeIsTest;
4348
4405
  exports.categoryService = categoryService;
4349
4406
  exports.chatService = chatService;
4350
4407
  exports.checkoutService = checkoutService;