@Test(groups = "slow") public void testCreateSuccessPurchaseWithPaymentControl() throws PaymentApiException, InvoiceApiException, EventBusException { final BigDecimal requestedAmount = BigDecimal.TEN; final UUID subscriptionId = UUID.randomUUID(); final UUID bundleId = UUID.randomUUID(); final LocalDate now = clock.getUTCToday(); final Invoice invoice = testHelper.createTestInvoice(account, now, Currency.USD); final String paymentExternalKey = invoice.getId().toString(); final String transactionExternalKey = "wouf wouf"; invoice.addInvoiceItem( new MockRecurringInvoiceItem( invoice.getId(), account.getId(), subscriptionId, bundleId, "test plan", "test phase", null, now, now.plusMonths(1), requestedAmount, new BigDecimal("1.0"), Currency.USD)); final Payment payment = paymentApi.createPurchaseWithPaymentControl( account, account.getPaymentMethodId(), null, requestedAmount, Currency.USD, paymentExternalKey, transactionExternalKey, createPropertiesForInvoice(invoice), INVOICE_PAYMENT, callContext); assertEquals(payment.getTransactions().size(), 1); assertEquals( payment.getTransactions().get(0).getTransactionStatus(), TransactionStatus.SUCCESS); assertEquals(payment.getTransactions().get(0).getTransactionType(), TransactionType.PURCHASE); final List<PaymentAttemptModelDao> attempts = paymentDao.getPaymentAttempts(paymentExternalKey, internalCallContext); assertEquals(attempts.size(), 1); final PaymentAttemptModelDao attempt = attempts.get(0); assertEquals(attempt.getStateName(), "SUCCESS"); // Ok now the fun part starts... we modify the attempt state to be 'INIT' and wait the the // Janitor to do its job. paymentDao.updatePaymentAttempt( attempt.getId(), attempt.getTransactionId(), "INIT", internalCallContext); final PaymentAttemptModelDao attempt2 = paymentDao.getPaymentAttempt(attempt.getId(), internalCallContext); assertEquals(attempt2.getStateName(), "INIT"); clock.addDays(1); try { Thread.sleep(1500); } catch (InterruptedException e) { } final PaymentAttemptModelDao attempt3 = paymentDao.getPaymentAttempt(attempt.getId(), internalCallContext); assertEquals(attempt3.getStateName(), "SUCCESS"); }
@Test(groups = "slow") public void testCreateSuccessRefundPaymentControlWithItemAdjustments() throws PaymentApiException, InvoiceApiException, EventBusException { final BigDecimal requestedAmount = BigDecimal.TEN; final UUID subscriptionId = UUID.randomUUID(); final UUID bundleId = UUID.randomUUID(); final LocalDate now = clock.getUTCToday(); final Invoice invoice = testHelper.createTestInvoice(account, now, Currency.USD); final String paymentExternalKey = invoice.getId().toString(); final String transactionExternalKey = "craboom"; final String transactionExternalKey2 = "qwerty"; final InvoiceItem invoiceItem = new MockRecurringInvoiceItem( invoice.getId(), account.getId(), subscriptionId, bundleId, "test plan", "test phase", null, now, now.plusMonths(1), requestedAmount, new BigDecimal("1.0"), Currency.USD); invoice.addInvoiceItem(invoiceItem); final Payment payment = paymentApi.createPurchaseWithPaymentControl( account, account.getPaymentMethodId(), null, requestedAmount, Currency.USD, paymentExternalKey, transactionExternalKey, createPropertiesForInvoice(invoice), INVOICE_PAYMENT, callContext); final List<PluginProperty> refundProperties = new ArrayList<PluginProperty>(); final HashMap<UUID, BigDecimal> uuidBigDecimalHashMap = new HashMap<UUID, BigDecimal>(); uuidBigDecimalHashMap.put(invoiceItem.getId(), new BigDecimal("1.0")); final PluginProperty refundIdsProp = new PluginProperty( InvoicePaymentRoutingPluginApi.PROP_IPCD_REFUND_IDS_WITH_AMOUNT_KEY, uuidBigDecimalHashMap, false); refundProperties.add(refundIdsProp); final Payment payment2 = paymentApi.createRefundWithPaymentControl( account, payment.getId(), null, Currency.USD, transactionExternalKey2, refundProperties, INVOICE_PAYMENT, callContext); assertEquals(payment2.getTransactions().size(), 2); PaymentTransaction refundTransaction = payment2.getTransactions().get(1); assertEquals(refundTransaction.getTransactionType(), TransactionType.REFUND); final List<PaymentAttemptModelDao> attempts = paymentDao.getPaymentAttempts(paymentExternalKey, internalCallContext); assertEquals(attempts.size(), 2); final PaymentAttemptModelDao refundAttempt = attempts.get(1); assertEquals(refundAttempt.getTransactionType(), TransactionType.REFUND); // Ok now the fun part starts... we modify the attempt state to be 'INIT' and wait the the // Janitor to do its job. paymentDao.updatePaymentAttempt( refundAttempt.getId(), refundAttempt.getTransactionId(), "INIT", internalCallContext); final PaymentAttemptModelDao attempt2 = paymentDao.getPaymentAttempt(refundAttempt.getId(), internalCallContext); assertEquals(attempt2.getStateName(), "INIT"); clock.addDays(1); try { Thread.sleep(1500); } catch (InterruptedException e) { } final PaymentAttemptModelDao attempt3 = paymentDao.getPaymentAttempt(refundAttempt.getId(), internalCallContext); assertEquals(attempt3.getStateName(), "SUCCESS"); }
@Override public void leavingState(final State state) throws OperationException { final DateTime utcNow = pluginControlPaymentAutomatonRunner.getClock().getUTCNow(); // Retrieve the associated payment transaction, if any PaymentTransactionModelDao paymentTransactionModelDaoCandidate = null; if (stateContext.getTransactionId() != null) { paymentTransactionModelDaoCandidate = paymentDao.getPaymentTransaction( stateContext.getTransactionId(), stateContext.getInternalCallContext()); Preconditions.checkNotNull( paymentTransactionModelDaoCandidate, "paymentTransaction cannot be null for id " + stateContext.getTransactionId()); } else if (stateContext.getPaymentTransactionExternalKey() != null) { final List<PaymentTransactionModelDao> paymentTransactionModelDaos = paymentDao.getPaymentTransactionsByExternalKey( stateContext.getPaymentTransactionExternalKey(), stateContext.getInternalCallContext()); if (!paymentTransactionModelDaos.isEmpty()) { paymentTransactionModelDaoCandidate = paymentTransactionModelDaos.get(paymentTransactionModelDaos.size() - 1); } } final PaymentTransactionModelDao paymentTransactionModelDao = paymentTransactionModelDaoCandidate != null && TRANSIENT_TRANSACTION_STATUSES.contains( paymentTransactionModelDaoCandidate.getTransactionStatus()) ? paymentTransactionModelDaoCandidate : null; if (stateContext.getPaymentId() != null && stateContext.getPaymentExternalKey() == null) { final PaymentModelDao payment = paymentDao.getPayment(stateContext.getPaymentId(), stateContext.getInternalCallContext()); Preconditions.checkNotNull( payment, "payment cannot be null for id " + stateContext.getPaymentId()); stateContext.setPaymentExternalKey(payment.getExternalKey()); stateContext.setPaymentMethodId(payment.getPaymentMethodId()); } else if (stateContext.getPaymentExternalKey() == null) { stateContext.setPaymentExternalKey(UUIDs.randomUUID().toString()); } if (paymentTransactionModelDao != null) { stateContext.setPaymentTransactionExternalKey( paymentTransactionModelDao.getTransactionExternalKey()); } else if (stateContext.getPaymentTransactionExternalKey() == null) { stateContext.setPaymentTransactionExternalKey(UUIDs.randomUUID().toString()); } if (stateContext.getPaymentMethodId() == null) { // Similar logic in PaymentAutomatonRunner stateContext.setPaymentMethodId(stateContext.getAccount().getPaymentMethodId()); } if (state.getName().equals(initialState.getName()) || state.getName().equals(retriedState.getName())) { try { final PaymentAttemptModelDao attempt; if (paymentTransactionModelDao != null && paymentTransactionModelDao.getAttemptId() != null) { attempt = pluginControlPaymentAutomatonRunner .getPaymentDao() .getPaymentAttempt( paymentTransactionModelDao.getAttemptId(), stateContext.getInternalCallContext()); Preconditions.checkNotNull( attempt, "attempt cannot be null for id " + paymentTransactionModelDao.getAttemptId()); } else { // // We don't serialize any properties at this stage to avoid serializing sensitive // information. // However, if after going through the control plugins, the attempt end up in RETRIED // state, // the properties will be serialized in the enteringState callback (any plugin that sets a // retried date is responsible to correctly remove sensitive information such as CVV, ...) // final byte[] serializedProperties = PluginPropertySerializer.serialize(ImmutableList.<PluginProperty>of()); attempt = new PaymentAttemptModelDao( stateContext.getAccount().getId(), stateContext.getPaymentMethodId(), utcNow, utcNow, stateContext.getPaymentExternalKey(), stateContext.getTransactionId(), stateContext.getPaymentTransactionExternalKey(), transactionType, initialState.getName(), stateContext.getAmount(), stateContext.getCurrency(), stateContext.getPaymentControlPluginNames(), serializedProperties); pluginControlPaymentAutomatonRunner .getPaymentDao() .insertPaymentAttemptWithProperties(attempt, stateContext.getInternalCallContext()); } stateContext.setAttemptId(attempt.getId()); } catch (final PluginPropertySerializerException e) { throw new OperationException(e); } } }