Class CommerceManager

java.lang.Object
com.codename1.payment.CommerceManager

public final class CommerceManager extends Object

Client facade for the Codename One Commerce service. It does not replace the Purchase API -- it wraps it: purchases still go through the platform store, and this class adds server-side validation and a store-agnostic entitlement check ("does this user have pro?") instead of per-SKU receipt juggling.

Typical use:

  CommerceManager cm = CommerceManager.getInstance();
  cm.setAppUserId(myAccountId);          // optional; stable per user
  cm.subscribe("pro_monthly");           // delegates to Purchase
  // later, off the EDT:
  cm.refresh();                          // validate receipts with the cloud
  if (cm.isEntitled("pro")) { ... }      // entitlement, not SKU

Graceful degradation. When the cloud is unreachable, or the developer's account is over its monthly validated-volume cap, the cloud stops validating and isDegraded() becomes true. Entitlement checks then fall back to the store-direct signal (the platform's own receipt), so a paying user is never locked out -- exactly the server-side behaviour, mirrored on the device.

The cloud endpoint, build key and package are stamped into the build by the Codename One build server when commerce is enabled (codename1.arg.commerce.cloud.enabled=true); when they are absent this class is inert and every call is a safe no-op that defers to Purchase.

  • Method Details

    • getInstance

      public static CommerceManager getInstance()
      The shared instance.
    • isCloudEnabled

      public boolean isCloudEnabled()
      Whether cloud commerce is active in this build. True for any cloud build (one that carries a build_key) unless the developer explicitly disabled it with codename1.commerce.cloud.enabled=false. In the simulator / a local build there is no build key, so this is false and every call safely defers to Purchase.
    • isDegraded

      public boolean isDegraded()
      True after a refresh() that could not get a server-validated answer -- the cloud was unreachable or the account is over its monthly cap. Entitlement checks fall back to the store-direct signal while this is true.
    • setAppUserId

      public void setAppUserId(String id)
      Sets the stable per-user id the cloud keys entitlements on. Pass your own account id once the user signs in; if never set, an anonymous id is generated and persisted so entitlements survive restarts on the device.
    • getAppUserId

      public String getAppUserId()
      The current (explicit or generated-anonymous) app user id.
    • logIn

      public boolean logIn(String newAppUserId)

      Tells the cloud that the current customer is now known as newAppUserId, typically when the user signs in to the application's own account. Purchases made under an anonymous id move to the account, so a subscription bought before signing in, or on another platform under the same account, unlocks here.

      Blocking network call -- invoke off the EDT. The id is switched only when the cloud accepted the change; on failure the manager is degraded, the previous id stays, and the call can be repeated. When the cloud is not enabled in this build the id is switched locally and nothing else happens.

      This id is commerce's own. It is independent of the user id of Analytics.setUserId, which stays whatever the application set: logging in or out here never changes it, and an application that wants its analytics under the same id sets it there as well.

      Parameters
      • newAppUserId: the application's stable id for the user
      Returns

      true if the id was switched

    • logOut

      public void logOut()
      Forgets the current customer: a new anonymous id is generated and the customer information becomes empty, so the next user of the device does not inherit the previous one's entitlements. Listeners are told. Nothing is sent to the cloud; call refresh() afterwards to pick up what the store account on the device still owns.
    • setAttributes

      public boolean setAttributes(Map attributes)

      Attaches attributes to the current customer in the cloud, where targeting rules and the console can read them. A null value deletes the attribute; any other value is sent as its string form.

      Blocking network call -- invoke off the EDT. Safe no-op when the cloud is not enabled in this build.

      Parameters
      • attributes: attribute names to values
      Returns

      true if the cloud accepted the attributes

    • restore

      public void restore()
      Asks the store to restore the purchases of the store account on this device, when the platform supports that. The restored receipts reach the cloud through the receipt synchronization that follows, which refreshes this manager by itself.
    • getCustomerInfo

      public CustomerInfo getCustomerInfo()

      The last customer information the cloud answered with. Never null: before any answer it is an empty customer.

      The last accepted answer is stored on the device, so this answers after a cold start and with no network. A stored entitlement is reported exactly as it was stored, even when its expiry date has passed by the device's clock: the device clock is the user's to set, so only the service's clock decides when an entitlement ends, and it does so on the next refresh().

    • addCustomerInfoListener

      public void addCustomerInfoListener(ActionListener l)
      Adds a listener that is called on the EDT whenever the customer information changes. The event's source is the new CustomerInfo.
    • removeCustomerInfoListener

      public void removeCustomerInfoListener(ActionListener l)
      Removes a listener added with addCustomerInfoListener(ActionListener).
    • purchase

      public void purchase(String sku)
    • subscribe

      public void subscribe(String sku)
    • unsubscribe

      public void unsubscribe(String sku)
    • isEntitled

      public boolean isEntitled(String entitlementId)
      Whether the user currently holds entitlementId. Uses the last cloud-validated answer when available; otherwise (cloud disabled or degraded) falls back to the store-direct signal, treating the entitlement id as a subscription SKU so a paying user keeps access.
    • getActiveEntitlements

      public List getActiveEntitlements()
      Snapshot of the entitlement ids currently considered active.
    • getOfferings

      public Offerings getOfferings()

      The offerings the cloud configured for this customer. Same as getOfferings(String) with no placement.

      Returns

      the offerings; never null, and empty when there is nothing to answer from

    • getOfferings

      public Offerings getOfferings(String placement)

      The offerings the cloud configured for this customer at a placement, a named place in the application ("onboarding", "settings") that the console can give an offering of its own.

      Blocking network call -- invoke off the EDT. The last accepted answer is stored on the device and is what this returns when the cloud cannot be reached. After a fresh answer the store is asked for the products of the packages, when the platform lists products, and each is attached to its CommercePackage.

      Parameters
      • placement: the placement, or null for the default offerings
      Returns

      the offerings; never null, and empty when the cloud is not enabled in this build or there is nothing to answer from

    • purchase

      public void purchase(CommercePackage pkg)

      Buys a package through the platform store: a subscription for every package that is one, a one time purchase otherwise. The outcome arrives the way every store purchase does, and through the customer information listeners once the cloud validated it.

      Parameters
      • pkg: the package to buy; null or a package with no product does nothing
    • isWebPurchase

      public boolean isWebPurchase(CommercePackage pkg)

      Whether purchase(CommercePackage) sells a package through a web checkout: the package has no product of this platform's store, and the platform has no store to sell one -- the browser and the desktop, where Purchase supports neither managed payments nor subscriptions.

      Parameters
      • pkg: the package asked about
      Returns

      true when buying the package opens a hosted checkout page

    • getWebPurchaseUrl

      public String getWebPurchaseUrl(CommercePackage pkg, String successUrl, String cancelUrl)

      Asks the cloud for a hosted checkout page that sells a package for its web price.

      Blocking network call -- invoke off the EDT.

      Parameters
      • pkg: the package to sell

      • successUrl: where the checkout sends the customer after paying, or null for the service's own page

      • cancelUrl: where the checkout sends a customer who gave up, or null for the service's own page

      Returns

      the address of the checkout page, or null when the cloud is not enabled in this build, could not be reached, or has no web price for the package

    • purchaseOnWeb

      public boolean purchaseOnWeb(CommercePackage pkg)

      Sells a package through a hosted checkout page, opened with Display.execute(String). The customer pays outside the application, so nothing reports the outcome here: once the application is back in the foreground the caller runs refresh() off the EDT, and the customer information listeners hear what was bought. PaywallView does that itself.

      May be called on the EDT, which keeps dispatching events while the cloud is asked for the page.

      Parameters
      • pkg: the package to sell
      Returns

      true when a checkout page was opened, false when there was none

    • purchaseWithPromotionalOffer

      public boolean purchaseWithPromotionalOffer(String productId, String offerId)

      Buys a subscription under one of its App Store promotional offers. The cloud signs the offer for this customer and the store is then asked to sell the product with that signature, as Purchase.purchase(String, PromotionalOffer) does.

      May be called on the EDT, which keeps dispatching events while the cloud is asked for the signature.

      Parameters
      • productId: the subscription product

      • offerId: the promotional offer of that product, as App Store Connect names it

      Returns

      true when the store was asked to sell the offer; false when no signature is available -- the platform's store has no promotional offers, the cloud is not enabled or it refused -- in which case nothing was bought

    • getManagementUrl

      public String getManagementUrl()
      The page where the customer manages the subscription, or null when there is none. Never touches the network: for a subscription bought on the web it is the billing portal fetchManagementUrl() last answered, and otherwise -- or before that was ever asked -- the store's page from the last customer information.
    • fetchManagementUrl

      public String fetchManagementUrl()

      The page where the customer manages the subscription, asking the cloud for a billing portal when the newest active entitlement -- the one that expires last -- was bought on the web (stripe or paddle). A portal address is short lived, so it is asked for when it is about to be opened. For a store subscription this is getManagementUrl() and nothing is asked.

      Blocking network call -- invoke off the EDT.

      Returns

      the address, or null when there is none

    • getCustomerCenterConfiguration

      public Map getCustomerCenterConfiguration()

      The customer center the console configured for this application, as parsed JSON: the paths to offer, the support address and the cancel reasons. CustomerCenter presents it.

      Blocking network call -- invoke off the EDT.

      Returns

      the configuration, or null when the cloud is not enabled in this build, could not be reached or refused to answer

    • logEvent

      public void logEvent(String type, String offeringId, String paywallId, String packageId)

      Reports a paywall event to the cloud, where the console charts the funnel of each offering and paywall. Same as logEvent(String, String, String, String, String) with no reason.

      Parameters
      • type: the event, such as IMPRESSION, PURCHASE_START, PURCHASE or CLOSE

      • offeringId: the offering shown, or null

      • paywallId: the paywall shown, or null

      • packageId: the package concerned, or null

    • logEvent

      public void logEvent(String type, String offeringId, String paywallId, String packageId, String reason)

      Reports an event to the cloud. The call returns at once on any thread: events are collected and sent together from a background thread, and an event that cannot be sent is dropped. Reporting never delays or fails a purchase. Safe no-op when the cloud is not enabled in this build.

      Parameters
      • type: the event, such as IMPRESSION or CANCEL_REASON

      • offeringId: the offering shown, or null

      • paywallId: the paywall shown, or null

      • packageId: the package concerned, or null

      • reason: the id of the reason chosen, for a CANCEL_REASON event, or null

    • analyticsEvent

      public void analyticsEvent(String name, String offeringId, String packageId, String productId, String key, String value)

      Reports a commerce event to the analytics providers the application registered with Analytics, in the category commerce. PaywallView and the customer center call this for what they show; an application that draws its own paywall can call it with the same names so its reports read the same.

      The event carries the parameters offering, package and product for the values that are not null, placement when the offering was fetched for a placement, and experiment and variant when the offering was chosen by an experiment. Nothing is sent without the user's analytics consent, and the call does nothing at all when no provider is registered. It never throws and never blocks.

      Parameters
      • name: the event name, such as paywall_view

      • offeringId: the offering concerned, or null

      • packageId: the package concerned, or null

      • productId: the store product concerned, or null

      • key: the name of one more parameter, or null

      • value: the value of that parameter, or null to leave it out

    • refresh

      public void refresh()

      Validates the device's current receipts with the cloud, then asks the cloud what the customer is entitled to and takes that answer as the entitlement state. The customer is asked even when the device holds no receipt at all, which is what unlocks a purchase made on another platform or granted from the console.

      Blocking network call -- invoke off the EDT. Safe no-op when the cloud is not enabled in this build. On any failure the manager flips to degraded and leaves the previous (or store-direct) answers in place.