Dual Pricing (Cash + Reward Points) Implementation in Moqui

Hello everyone,

I’d like to share a use case we’re implementing at my organization and get your feedback on the proposed approach.


Use Case

We need to support dual pricing for a single product, allowing customers to choose between:

  1. Full cash payment (e.g. $20.00)
  2. Mixed payment of cash plus reward points (e.g. $10.00 + 10 points)

This must work seamlessly from product setup through checkout, order processing, and ledger reconciliation.

Proposed Solution

1. Product-Level Data Model

  • mantle.product.price.ProductPrice

    • Two records per product:
      • DEFAULT_PRICE → currency-only (unitPrice = 20.00, currencyUomId = USD)
      • HYBRID_PRICE → cash portion (unitPrice = 10.00, currencyUomId = USD)
  • Custom entity: ProductPriceAttribute
    Stores the points requirement for the mixed option.

    <entity entity-name="ProductPriceAttribute" package="noi.product">
        <field name="productId" type="id" is-pk="true"/>
        <field name="priceTypeEnumId" type="id" is-pk="true"/>
        <field name="attrName" type="text-short" is-pk="true"/>
        <field name="attrValue" type="text-medium"/>
        <field name="fromDate" type="date-time" is-pk="true"/>
        <field name="thruDate" type="date-time"/>
    </entity>
    
    <!-- Example Seed data -->
    <noi.product.ProductPriceAttribute
            productId="MY_PROD_001"
            priceTypeEnumId="HYBRID_PRICE"
            attrName="rewardPoints"
            attrValue="10"
            fromDate="2025-05-01T00:00:00"/>
    

2. Order-Level Handling

  • Custom entity: OrderAdjustment
    To record point redemptions at checkout:

    <entity entity-name="OrderAdjustment" package="noi.order">
        <field name="orderAdjustmentId" type="id" is-pk="true"/>
        <field name="orderId" type="id"/>
        <field name="orderItemSeqId" type="id"/>
        <field name="orderAdjustmentTypeEnumId" type="id"/>
        <field name="amount" type="number-decimal"/>
        <field name="comments" type="text-long"/>
        <field name="adjustmentDate" type="date-time" default="ec.user.nowTimestamp"/>
    </entity>
    
    <!-- Example Seed data -->
    <noi.order.OrderAdjustment
            orderId="${orderId}"
            orderItemSeqId="01"
            orderAdjustmentTypeEnumId="REWARD_POINT_REDEMPTION"
            amount="-10"
            comments="Redeemed 10 points"
            adjustmentDate="${ec.user.nowTimestamp}"/>
    
  • Custom entity: OrderPaymentPreference
    To split payment between points and cash:

    <entity entity-name="OrderPaymentPreference" package="noi.order">
        <field name="orderPaymentPreferenceId" type="id" is-pk="true"/>
        <field name="organizationId" type="id"/>
        <field name="paymentMethodTypeEnumId" type="id"/>
        <field name="maxAmount" type="number-decimal"/>
        <field name="sequenceNum" type="number-integer"/>
    </entity>
    
    <!-- Example Seed data -->
    <!-- First: redeem points -->
    <noi.order.OrderPaymentPreference
        organizationId="${partyId}"
        paymentMethodTypeEnumId="FIN_ACCOUNT_LOYALTY_POINTS"
        maxAmount="10"
        sequenceNum="1"/>
    <!-- Second: cash payment -->
    <noi.order.OrderPaymentPreference
        orderId="${orderId}"
        paymentMethodTypeEnumId="CREDIT_CARD"
        sequenceNum="2"/>
    

OOTB Entities to Use / Extend

Layer Entity / Type Role
Product ProductPrice Store both price types
Order OrderItem Capture unitPrice and chosen price type
Payment FinAccountType / FinAccount Track user point balances

All other supporting tables (ProductPriceAttribute, OrderAdjustment, OrderPaymentPreference) are custom additions defined above.

Request for Feedback

  • Has anyone implemented a similar “cash + points” or mixed-currency pricing in Moqui?
  • Any suggestions for more elegant integration with promotions or loyalty modules?

Your insights or experiences would be greatly appreciated!

1 Like

Given an order with a currencyUomId and grandTotal, the total sum of authorized payments must be equal to the grandTotal: mantle-usl/service/mantle/order/OrderInfoServices.xml at 8503eeda6bfb7ee8c3207a4fda61487fc42895a7 · moqui/mantle-usl · GitHub.

There can be multiple payments of different currency types: mantle-udm/entity/AccountingAccountEntities.xml at dcca23b4cd2819a776d447b36fed9102ec7f4844 · moqui/mantle-udm · GitHub

You would just need to have a UomConversion between your custom currency (i.e. yourbrand’s reward points) and the underlying order currency uom like USD.

There will need to be some code changes for this to be possible, but with a local UomConversion it should be pretty simple to do.

BTW, I wouldn’t change the price, I would change the payment for the price. When you go to a store and purchase with loyalty points, the price tag in USD doesn’t change.

1 Like

Thank you so much for sharing your thoughts @michael - your approach makes perfect sense for both checkout and order processing. Here’s how I’m planning to incorporate it:


1. Two Order Items per Order

Rather than changing the product price itself, we’ll represent the cash + points scenario with two order items:

  1. Product item at the full USD price
  2. Discount item where we convert redeemed points to their USD equivalent via our UoM conversion

This keeps the order’s grandTotal in USD and satisfies the requirement that “sum of authorized payments = grandTotal.”


2. Payments and Point Redemptions

  • Cash payment: We’ll only create a record in the Payment table for the USD portion of the order.
  • Reward points: The point portion will be handled entirely in the FinancialAccountTrans table, debiting the customer’s loyalty FinAccount by the points redeemed.

3. Displaying Dual Prices in the Storefront

For our product listings and PDPs, we still need to show both:

  • Current price (e.g. $20.00)
  • Cash + points price (e.g. $10.00 + 10 pts)

To support this, I plan to introduce a small custom entity, ProductPriceAttribute, to hold the “cash + points” value. I’m open to suggestions if anyone has alternative patterns here!


4. Rewards Points Flow

The rewards points redemption will be fully recorded in our FinancialAccountTrans ledger, ensuring we have a clear audit trail of point balances and redemptions.


Any thoughts on the custom ProductPriceAttribute approach for ecommerce listings would be especially welcome!

1 Like

Hi @nirendra! This seems like a standard reward points functionality that a lot of ecom stores have. Your 4 points above seem sane, we also handle reward points using a similar flow. Regarding point 3 though, why do you want to show 2 prices in the storefront? I haven’t seen any store do that. Usually they just say 1 point = 1 USD or whatever other value, and the customer then calculates the price depending on how many points he has.

2 Likes

Hi @grozadanut! Thanks for the feedback. In India (and several other markets), showing both cash and points prices side-by-side is actually pretty common in e-commerce. It helps customers immediately see the value of their points and simplifies the checkout decision—marketing teams love it because it drives engagement and retention.

1 Like

Ah, so it’s a fixed number of points, I see. Well, I don’t think there is support for multiple prices in Moqui at this point, but if a generic solution would be implemented I would also be interested to contribute. In my case I want to show different prices for different UOMs(eg: 10 USD/sqm or 5 USD/piece).

In fact, the data model already supports multiple prices(ProductPrice entity), for example list price, current price, discounted price for volume… So I guess it’s a matter of just UI to show multiple prices on the product page.

For your case though, I don’t think the model supports point based pricing, so I guess you need to handle the points separately, for example in the ProductPriceAttribute as you just described.

2 Likes

Seems like if you want to do multiple currency prices, you would just have one base currency price, and calculate a set value of virtual money based on some percentage of the total price. You could also have a maximum percentage of the product price available to spend via a certain currency (like your points).

1 Like

You’re welcome @nirendra! I’m glad that helps!

1 Like