Subversion Repositories SmartDukaan

Rev

Rev 3010 | Rev 3126 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1905 chandransh 1
package in.shop2020.serving.services;
2
 
3
import in.shop2020.model.v1.order.LineItem;
4
import in.shop2020.model.v1.order.Order;
5
import in.shop2020.model.v1.order.Transaction;
6
import in.shop2020.model.v1.order.TransactionServiceException;
7
import in.shop2020.model.v1.order.TransactionStatus;
8
import in.shop2020.model.v1.user.Cart;
9
import in.shop2020.model.v1.user.Line;
1981 varun.gupt 10
import in.shop2020.model.v1.user.PromotionException;
1905 chandransh 11
import in.shop2020.model.v1.user.ShoppingCartException;
2137 chandransh 12
import in.shop2020.payments.Payment;
1905 chandransh 13
import in.shop2020.payments.PaymentException;
14
import in.shop2020.serving.utils.Utils;
15
import in.shop2020.thrift.clients.PaymentServiceClient;
1981 varun.gupt 16
import in.shop2020.thrift.clients.PromotionServiceClient;
1905 chandransh 17
import in.shop2020.thrift.clients.TransactionServiceClient;
18
import in.shop2020.thrift.clients.UserContextServiceClient;
19
 
20
import java.util.HashMap;
21
import java.util.List;
22
import java.util.Map;
23
 
24
import org.apache.log4j.Logger;
25
import org.apache.thrift.TException;
26
 
27
/**
28
 * This class has methods to be used to process non-gateway-specific aspects of
29
 * payments and transactions.
30
 * 
31
 * @author Chandranshu
32
 * 
33
 */
34
public class CommonPaymentService {
35
 
36
	private static final boolean PAYMENT_NOT_CREATED = false;
37
 
38
	private static Logger log = Logger.getLogger(Class.class);
39
 
40
	private long paymentId;
41
	private double amount;
42
 
43
	public long getPaymentId() {
44
		return paymentId;
45
	}
46
 
47
	public double getAmount() {
48
		return amount;
49
	}
50
 
51
	/**
52
	 * Creates a payment for the given cart of the given user for the given
53
	 * transaction. Stores the id of the newly created payment and the amount
54
	 * for which this payment was created. They can be retrieved later on using
55
	 * {@link #getPaymentId()}getPaymentId() and {@link #getAmount()}getAmount()
56
	 * methods respectively later on.
57
	 * 
58
	 * @param currentCartId
59
	 *            The cart for which the payment object has to be created.
60
	 * @param userId
61
	 *            The user for whom the payment has to be created.
62
	 * @param txnId
63
	 *            The transaction against which the payment has to be created.
64
	 * @param gatewayId
65
	 * @return True if the payment object is successfully created, False
66
	 *         otherwise.
67
	 */
68
	public boolean createPayment(long currentCartId, long userId, long txnId, int gatewayId){
69
		PaymentServiceClient paymentServiceClient = null;
70
		try {
71
			paymentServiceClient = new PaymentServiceClient();
72
		} catch (Exception e) {
2199 chandransh 73
			log.error("Error while getting payment client", e);
1905 chandransh 74
			return PAYMENT_NOT_CREATED;
75
		}
76
 
77
		try {
78
			amount = calculatePaymentAmount(currentCartId);
79
		} catch (ShoppingCartException e1) {
2199 chandransh 80
			log.error("Unable to fetch payment amount from cart id.", e1);
1905 chandransh 81
			return PAYMENT_NOT_CREATED;
82
		} catch (TException e1) {
2199 chandransh 83
			log.error("Unable to fetch payment amount.", e1);
1905 chandransh 84
			return PAYMENT_NOT_CREATED;
85
		}
86
 
87
		try {
2137 chandransh 88
			paymentId = paymentServiceClient.getClient().createPayment(userId, amount, gatewayId, txnId);
89
			// This is being done to ensure that the amount which we pass on to
90
			// the PGs is same as what we have in the database.
91
			Payment payment = paymentServiceClient.getClient().getPayment(paymentId);
92
			amount = payment.getAmount();
1905 chandransh 93
		} catch (PaymentException e1) {
2199 chandransh 94
			log.error("Unable to create payment object.", e1);
1905 chandransh 95
			return PAYMENT_NOT_CREATED;
96
		} catch (TException e) {
2199 chandransh 97
			log.error("Not able to create payment object.", e);
1905 chandransh 98
			return PAYMENT_NOT_CREATED;
99
		}
100
 
101
		return true;
102
	}
103
 
2199 chandransh 104
	// TODO: The service client parameters in the processSuccessfulTxn et al are
105
	// unnecessary but initializing them again when the caller has the necessary
106
	// references has a performance overhead. Need to think more about this.
107
 
1905 chandransh 108
	/**
2199 chandransh 109
	 * Processes a successful transaction by:
110
	 * <ol>
3063 chandransh 111
	 * <li>Marking the given transaction as 'authorized'.</li>
2199 chandransh 112
	 * <li>Removing the items in the cart for which the given transaction was
113
	 * processed.</li>
114
	 * <li>Marking the coupon associated with this transaction, if any, as used
115
	 * for this user.</li>
116
	 * <li>Queuing the transaction successful email, containing transaction
117
	 * info, to be sent later by a batch job.</li>
118
	 * </ol>
119
	 * <br>
1905 chandransh 120
	 * Please note that it's possible that a user has added items to the cart
2199 chandransh 121
	 * and so it's not possible to simply wipe out their cart. Therefore, it's
122
	 * important to ensure that we remove only as much quantity of items as for
123
	 * which the order was processed.
1905 chandransh 124
	 * 
2199 chandransh 125
	 * @param txnId
126
	 *            The transaction which should be marked as successful.
127
	 * @param userServiceClient
128
	 *            A user context service client to use.
129
	 * @param transactionServiceClient
130
	 *            A transaction service client to use.
1905 chandransh 131
	 */
132
	public static void processSuccessfulTxn(long txnId, UserContextServiceClient userServiceClient, TransactionServiceClient transactionServiceClient) {
133
		Transaction transaction = null;
134
		try {
135
			in.shop2020.model.v1.order.TransactionService.Client transactionClient = transactionServiceClient.getClient();
3010 chandransh 136
			transactionClient.changeTransactionStatus(txnId, TransactionStatus.AUTHORIZED, "Payment authorized for the order");
1905 chandransh 137
			transaction = transactionClient.getTransaction(txnId);
2199 chandransh 138
			transactionClient.enqueueTransactionInfoEmail(txnId);
1905 chandransh 139
		} catch (TException e1) {
2199 chandransh 140
			log.error("Unable to update status of transaction. Thrift Exception:", e1);
1905 chandransh 141
		} catch (TransactionServiceException e) {
2199 chandransh 142
			log.error("Unable to update status of transaction. Thrift Exception: ", e);
1905 chandransh 143
		}
2219 varun.gupt 144
		trackCouponUsage(transaction);
2199 chandransh 145
        resetCart(transaction, userServiceClient);
1905 chandransh 146
	}
147
 
148
	/**
149
	 * Marks a transaction as well as all its orders as failed.
150
	 * 
151
	 * @param txnId
152
	 *            The id of the transaction which has to be marked as failed.
153
	 * @param transactionServiceClient
154
	 */
155
	public static void processFailedTxn(long txnId, TransactionServiceClient transactionServiceClient) {
156
		try {
157
			in.shop2020.model.v1.order.TransactionService.Client transactionClient = transactionServiceClient.getClient();
158
			transactionClient.changeTransactionStatus(txnId, TransactionStatus.FAILED, "Payment failed for the transaction.");
159
		} catch(TException e){
2199 chandransh 160
			log.error("Thrift exception while getting information from transaction service.", e);
1905 chandransh 161
		} catch (TransactionServiceException e) {
2199 chandransh 162
			log.error("Error while updating status information in transaction database.", e);
1905 chandransh 163
		}
164
	}
165
 
3063 chandransh 166
    /**
167
     * Processes a COD transaction by:
168
     * <ol>
169
     * <li>Setting the COD flag of all the orders and moving them to the INIT
170
     * state.
171
     * <li>Marking the given transaction to be in COD_IN_PROCESS state
172
     * <li>Marking the coupon associated with this transaction, if any, as used
173
     * for this user.</li>
174
     * <li>Queuing the transaction successful email, containing transaction
175
     * info, to be sent later by a batch job.</li>
176
     * </ol>
177
     * <br>
178
     * Please note that it's possible that a user has added items to the cart
179
     * and so it's not possible to simply wipe out their cart. Therefore, it's
180
     * important to ensure that we remove only as much quantity of items as for
181
     * which the order was processed.
182
     * 
183
     * @param txnId
184
     *            The COD transaction which should be marked as verification
185
     *            pending.
186
     */
187
	public static void processCodTxn(long txnId){
188
        try {            
189
            TransactionServiceClient transactionServiceClient = new TransactionServiceClient();
190
            in.shop2020.model.v1.order.TransactionService.Client transactionClient = transactionServiceClient.getClient();
191
            transactionClient.changeTransactionStatus(txnId, TransactionStatus.COD_IN_PROCESS, "COD payment awaited");
192
            Transaction transaction = transactionClient.getTransaction(txnId);
193
            transactionClient.enqueueTransactionInfoEmail(txnId);
194
 
195
            UserContextServiceClient userServiceClient = new UserContextServiceClient();
196
            trackCouponUsage(transaction);
197
            resetCart(transaction, userServiceClient);
198
        } catch (TException e1) {
199
            log.error("Unable to update status of transaction. Thrift Exception:", e1);
200
        } catch (TransactionServiceException e) {
201
            log.error("Unable to update status of transaction. Thrift Exception: ", e);
202
        } catch (Exception e) {
203
            log.error("Unable to update status of transaction. Thrift Exception: ", e);
204
        }
205
	}
206
 
1905 chandransh 207
	/**
208
	 * Calculates the amount for the payment required for the given cart.
209
	 * 
210
	 * @param cartId
211
	 *            Id of the cart for which this payment amount has to be
212
	 *            calculated.
213
	 * @return The total amount for which a payment should be created.
214
	 * @throws ShoppingCartException
215
	 *             If no cart can be found for the given id.
216
	 * @throws TException
217
	 */
218
	private double calculatePaymentAmount(long cartId) throws ShoppingCartException, TException{
219
		double totalAmount = 0;
220
		Cart cart;
221
		UserContextServiceClient userContextServiceClient = null;
222
		try {
223
			userContextServiceClient = new UserContextServiceClient();
224
		} catch (Exception e) {
2942 chandransh 225
			log.error("Unable to initialize user context service client", e);
226
			throw new ShoppingCartException(100, "Unable to initialize the user service client");
1905 chandransh 227
		}
228
		in.shop2020.model.v1.user.UserContextService.Client userClient = userContextServiceClient.getClient();
229
		cart = userClient.getCart(cartId);
1981 varun.gupt 230
 
2199 chandransh 231
		if(cart.getCouponCode() == null)  {
1981 varun.gupt 232
		    List<Line> lineItems = cart.getLines(); 
233
 
234
	        for (Line line : lineItems) {
235
	            long productId = line.getItemId();
236
	            // FIXME: It's expensive to get the price of each item from the
237
	            // catalog service. We maintain the pricing info in the line items
238
	            // themselves now.
239
	            totalAmount =  totalAmount + line.getQuantity() * Utils.getItemPrice(productId);
240
	        }
2199 chandransh 241
		} else {
1981 varun.gupt 242
		    totalAmount = cart.getDiscountedPrice();
1905 chandransh 243
		}
1981 varun.gupt 244
 
1905 chandransh 245
		return totalAmount;
246
	}
247
 
2199 chandransh 248
	/**
249
	 * Removes the items processed through the given transaction from the
250
	 * shopping cart.
251
	 * 
252
	 * @param transaction
253
	 *            The transaction whose items have to be removed from the
254
	 *            shopping cart.
255
	 * @param userServiceClient
256
	 */
257
	private static void resetCart(Transaction transaction, UserContextServiceClient userServiceClient) {
258
		Map<Long, Double> items = new HashMap<Long, Double>();
259
		for(Order order: transaction.getOrders()){
260
			for(LineItem lineitem: order.getLineitems()){
261
				Long itemId = lineitem.getItem_id();
262
				Double quantity = items.get(itemId);
263
				if(quantity==null){
264
					quantity = lineitem.getQuantity();
265
				} else {
266
					quantity= quantity + lineitem.getQuantity();
267
				}
268
				items.put(itemId, quantity);
269
			}
270
		}
271
 
3063 chandransh 272
		log.debug("Items to reset in cart are: " + items);
2199 chandransh 273
 
274
		try {
275
			//TODO Optimize the function to send less data over the wire
276
            userServiceClient.getClient().resetCart(transaction.getShoppingCartid(), items);
277
		}catch (TException e) {
278
			log.error("Error while updating information in payment database.", e);
279
		} catch (ShoppingCartException e) {
280
			log.error("Error while reseting the cart in cart database.", e);
281
		}catch (Exception e) {
282
			log.error("Unexpected exception", e);
283
        }
284
	}
285
 
286
	/**
287
	 * Mark the coupon associated with the given transaction as used.
288
	 * 
289
	 * @param transaction
290
	 *            The transaction to track coupon for.
291
	 * 
292
	 * @param userServiceClient
293
	 *            The user service client instance to use.
294
	 */
2219 varun.gupt 295
	private static void trackCouponUsage(Transaction transaction) {
2199 chandransh 296
        try {
2219 varun.gupt 297
            String couponCode = transaction.getCoupon_code();
298
 
2199 chandransh 299
            if (couponCode != null && !couponCode.isEmpty()) {
300
            	PromotionServiceClient promotionServiceClient = new PromotionServiceClient();
301
                promotionServiceClient.getClient().trackCouponUsage(couponCode, transaction.getId(), transaction.getCustomer_id());
302
            }
303
        } catch (PromotionException e) {
304
            log.error("Promotion Exception: " + e);
305
        } catch (TException e)  {
306
            log.error("Transport from Promotion Service failed:", e);
307
        } catch (Exception e) {
308
            log.error("Unexpected exception:", e);
309
        }
310
	}
1905 chandransh 311
}