@Override
 public void allowPayout(
     Context respCtx,
     Game game,
     PayoutLevelAllowRequest[] levelAllowRequests,
     BigDecimal actualPayout)
     throws ApplicationException {
   // verify whether the game has been allocated
   MerchantCommission comm =
       this.getMerchantCommissionDao()
           .getByMerchantAndGame(respCtx.getTransaction().getMerchantId(), game.getId());
   if (comm == null || !comm.isAllowPayout()) {
     throw new SystemException(
         SystemException.CODE_OPERATOR_PAYOUT_NOPRIVILEDGE,
         "operator(id="
             + respCtx.getOperatorId()
             + ") has no priviledge to payout ticket of game '"
             + game.getId()
             + "', allocate the game to its merchant(id="
             + respCtx.getTransaction().getMerchantId()
             + ") first.");
   }
   GameType gameType = GameType.fromType(game.getType());
   // only need to check the prize group constraints of operator
   PrizeGroup prizeGroup = respCtx.getOperator().getPrizeGroup();
   if (!prizeGroup.isPayoutAllowed()) {
     throw new ApplicationException(
         SystemException.CODE_MERCHANT_UNALLOWED_PAY,
         "Operator(" + respCtx.getOperator() + ") doesn't allow payout.");
   }
   // ** both conditions must be satisfied
   // by payout limit
   if (new BigDecimal("0").compareTo(actualPayout) != 0) {
     this.verifyPayoutByLimit(respCtx, actualPayout, respCtx.getOperator());
   }
   // for odds and fix-prize game, no need to check prize level.
   if (!gameType.isFixedPrize()) {
     // by prize level
     if (levelAllowRequests != null) {
       for (PayoutLevelAllowRequest levelAllowRequest : levelAllowRequests) {
         this.verifyPayoutByLevel(
             respCtx,
             respCtx.getOperator(),
             levelAllowRequest.getRequestedPrizeLevels(),
             levelAllowRequest.getGameType(),
             levelAllowRequest.getPrizeGroupType());
       }
     }
   }
 }
 /**
  * When cash out, system check the cashout-limit of the merchant.
  *
  * @param respCtx The context of cashout transaction.
  * @param actualCashout The actual amount of cashout.
  * @throws ApplicationException when encounter any business exception.
  */
 protected void allowCashoutByLimit(Context respCtx, BigDecimal actualCashout)
     throws ApplicationException {
   PrizeGroup cashoutGroup = respCtx.getOperator().getCashoutGroup();
   if (cashoutGroup == null) {
     throw new ApplicationException(
         SystemException.CODE_MERCHANT_UNALLOWED_CASHOUT,
         "Operator("
             + respCtx.getOperator()
             + ") can't perform cashout, no any cashout group definition found.");
   }
   if (cashoutGroup.getMaxPayoutAmount() != null
       && cashoutGroup.getMaxPayoutAmount().compareTo(new BigDecimal("0")) >= 0) {
     if (actualCashout.compareTo(cashoutGroup.getMaxPayoutAmount()) > 0) {
       throw new ApplicationException(
           SystemException.CODE_MERCHANT_UNALLOWED_CASHOUT_SCOPE,
           "The prize actual amount("
               + actualCashout
               + ") exceed the max "
               + "allowed cashout amount("
               + cashoutGroup.getMaxPayoutAmount()
               + ") of the Operator("
               + respCtx.getOperator()
               + ").");
     }
   }
   if (cashoutGroup.getMinPayoutAmount() != null
       && cashoutGroup.getMinPayoutAmount().compareTo(new BigDecimal("0")) >= 0) {
     if (actualCashout.compareTo(cashoutGroup.getMinPayoutAmount()) < 0) {
       throw new ApplicationException(
           SystemException.CODE_MERCHANT_UNALLOWED_CASHOUT_SCOPE,
           "The prize actual amount("
               + actualCashout
               + ") is less than min cashout amount("
               + cashoutGroup.getMinPayoutAmount()
               + ") of Operator("
               + respCtx.getOperator()
               + ").");
     }
   }
 }
 /**
  * When cash out, a merchant can only cashout a given max amount.
  *
  * @param respCtx The context of cashout transaction.
  * @param actualCashout The actual amount of cashout.
  * @throws ApplicationException when encounter any business exception.
  */
 protected void allowDailyCashout(Context respCtx, BigDecimal actualCashout)
     throws ApplicationException {
   PrizeGroup cashoutGroup = respCtx.getOperator().getCashoutGroup();
   if (cashoutGroup == null) {
     throw new ApplicationException(
         SystemException.CODE_MERCHANT_UNALLOWED_CASHOUT,
         "Operator("
             + respCtx.getOperator()
             + ") can't perform cashout, no any cashout "
             + "group definition found.");
   }
   if (logger.isDebugEnabled()) {
     logger.debug(
         "Before cash out[dailyCashoutLevel: "
             + respCtx.getOperator().getDailyCashoutLevel()
             + ", dailyCashoutLimit: "
             + cashoutGroup.getDailyCashoutLimit()
             + ", cashoutAmount: "
             + actualCashout
             + "] of Operator( "
             + respCtx.getOperator()
             + ").");
   }
   if (actualCashout
           .add(respCtx.getOperator().getDailyCashoutLevel())
           .compareTo(cashoutGroup.getDailyCashoutLimit())
       > 0) {
     throw new ApplicationException(
         SystemException.CODE_EXCEED_ALLOWED_MERCHANT_DAILY_CASHOUT_LIMIT,
         "Cash out amount("
             + actualCashout
             + ") + current cashout level("
             + respCtx.getOperator().getDailyCashoutLevel()
             + ") has exceeded allowed limit("
             + cashoutGroup.getDailyCashoutLimit()
             + ") of Operator("
             + respCtx.getOperator()
             + ").");
   }
 }