use of org.killbill.billing.payment.api.PluginProperty in project killbill by killbill.
the class TestJanitor method testCreateSuccessRefundPaymentControlWithItemAdjustments.
@Test(groups = "slow")
public void testCreateSuccessRefundPaymentControlWithItemAdjustments() throws Exception {
final BigDecimal requestedAmount = BigDecimal.TEN;
final UUID subscriptionId = UUID.randomUUID();
final UUID bundleId = UUID.randomUUID();
final LocalDate now = clock.getUTCToday();
testListener.pushExpectedEvent(NextEvent.INVOICE);
final Invoice invoice = testHelper.createTestInvoice(account, now, Currency.USD);
testListener.assertListenerStatus();
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);
testListener.pushExpectedEvent(NextEvent.PAYMENT);
final Payment payment = paymentApi.createPurchaseWithPaymentControl(account, account.getPaymentMethodId(), null, requestedAmount, Currency.USD, paymentExternalKey, transactionExternalKey, createPropertiesForInvoice(invoice), INVOICE_PAYMENT, callContext);
testListener.assertListenerStatus();
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(InvoicePaymentControlPluginApi.PROP_IPCD_REFUND_IDS_WITH_AMOUNT_KEY, uuidBigDecimalHashMap, false);
refundProperties.add(refundIdsProp);
testListener.pushExpectedEvent(NextEvent.PAYMENT);
final Payment payment2 = paymentApi.createRefundWithPaymentControl(account, payment.getId(), null, Currency.USD, transactionExternalKey2, refundProperties, INVOICE_PAYMENT, callContext);
testListener.assertListenerStatus();
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);
await().atMost(TIMEOUT, TimeUnit.SECONDS).until(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
final PaymentAttemptModelDao attempt3 = paymentDao.getPaymentAttempt(refundAttempt.getId(), internalCallContext);
return "SUCCESS".equals(attempt3.getStateName());
}
});
}
use of org.killbill.billing.payment.api.PluginProperty in project killbill by killbill.
the class TestJanitor method createPropertiesForInvoice.
private List<PluginProperty> createPropertiesForInvoice(final Invoice invoice) {
final List<PluginProperty> result = new ArrayList<PluginProperty>();
result.add(new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_INVOICE_ID, invoice.getId().toString(), false));
return result;
}
use of org.killbill.billing.payment.api.PluginProperty in project killbill by killbill.
the class TestRetryService method createPropertiesForInvoice.
private List<PluginProperty> createPropertiesForInvoice(final Invoice invoice) {
final List<PluginProperty> result = new ArrayList<PluginProperty>();
result.add(new PluginProperty(InvoicePaymentControlPluginApi.PROP_IPCD_INVOICE_ID, invoice.getId().toString(), false));
return result;
}
use of org.killbill.billing.payment.api.PluginProperty in project killbill by killbill.
the class InvoicePaymentControlPluginApi method onSuccessCall.
@Override
public OnSuccessPaymentControlResult onSuccessCall(final PaymentControlContext paymentControlContext, final Iterable<PluginProperty> pluginProperties) throws PaymentControlApiException {
final TransactionType transactionType = paymentControlContext.getTransactionType();
Preconditions.checkArgument(transactionType == TransactionType.PURCHASE || transactionType == TransactionType.REFUND || transactionType == TransactionType.CHARGEBACK || transactionType == TransactionType.CREDIT);
final InternalCallContext internalContext = internalCallContextFactory.createInternalCallContext(paymentControlContext.getAccountId(), paymentControlContext);
try {
final InvoicePayment existingInvoicePayment;
switch(transactionType) {
case PURCHASE:
final UUID invoiceId = getInvoiceId(pluginProperties);
existingInvoicePayment = invoiceApi.getInvoicePaymentForAttempt(paymentControlContext.getPaymentId(), internalContext);
if (existingInvoicePayment != null && existingInvoicePayment.isSuccess()) {
// Only one successful purchase per payment (the invoice could be linked to multiple successful payments though)
log.info("onSuccessCall was already completed for purchase paymentId='{}'", paymentControlContext.getPaymentId());
} else {
final BigDecimal invoicePaymentAmount;
if (paymentControlContext.getCurrency() == paymentControlContext.getProcessedCurrency()) {
invoicePaymentAmount = paymentControlContext.getProcessedAmount();
} else {
log.warn("processedCurrency='{}' of invoice paymentId='{}' doesn't match invoice currency='{}', assuming it is a full payment", paymentControlContext.getProcessedCurrency(), paymentControlContext.getPaymentId(), paymentControlContext.getCurrency());
invoicePaymentAmount = paymentControlContext.getAmount();
}
final PaymentTransactionModelDao paymentTransactionModelDao = paymentDao.getPaymentTransaction(paymentControlContext.getTransactionId(), internalContext);
// If it's not SUCCESS, it is PENDING
final boolean success = paymentTransactionModelDao.getTransactionStatus() == TransactionStatus.SUCCESS;
log.debug("Notifying invoice of {} paymentId='{}', amount='{}', currency='{}', invoiceId='{}'", success ? "successful" : "pending", paymentControlContext.getPaymentId(), invoicePaymentAmount, paymentControlContext.getCurrency(), invoiceId);
// For PENDING payments, the attempt will be kept as unsuccessful and an InvoicePaymentErrorInternalEvent sent on the bus (e.g. for Overdue)
invoiceApi.recordPaymentAttemptCompletion(invoiceId, invoicePaymentAmount, paymentControlContext.getCurrency(), paymentControlContext.getProcessedCurrency(), paymentControlContext.getPaymentId(), paymentControlContext.getTransactionExternalKey(), paymentControlContext.getCreatedDate(), success, internalContext);
}
break;
case REFUND:
final Map<UUID, BigDecimal> idWithAmount = extractIdsWithAmountFromProperties(pluginProperties);
final PluginProperty prop = getPluginProperty(pluginProperties, PROP_IPCD_REFUND_WITH_ADJUSTMENTS);
final boolean isAdjusted = prop != null ? Boolean.valueOf((String) prop.getValue()) : false;
invoiceApi.recordRefund(paymentControlContext.getPaymentId(), paymentControlContext.getAmount(), isAdjusted, idWithAmount, paymentControlContext.getTransactionExternalKey(), internalContext);
break;
case CHARGEBACK:
existingInvoicePayment = invoiceApi.getInvoicePaymentForChargeback(paymentControlContext.getPaymentId(), internalContext);
if (existingInvoicePayment != null) {
// We don't support partial chargebacks (yet?)
log.info("onSuccessCall was already completed for chargeback paymentId='{}'", paymentControlContext.getPaymentId());
} else {
final InvoicePayment linkedInvoicePayment = invoiceApi.getInvoicePaymentForAttempt(paymentControlContext.getPaymentId(), internalContext);
final BigDecimal amount;
final Currency currency;
if (linkedInvoicePayment.getCurrency().equals(paymentControlContext.getProcessedCurrency()) && paymentControlContext.getProcessedAmount() != null) {
amount = paymentControlContext.getProcessedAmount();
currency = paymentControlContext.getProcessedCurrency();
} else if (linkedInvoicePayment.getCurrency().equals(paymentControlContext.getCurrency()) && paymentControlContext.getAmount() != null) {
amount = paymentControlContext.getAmount();
currency = paymentControlContext.getCurrency();
} else {
amount = linkedInvoicePayment.getAmount();
currency = linkedInvoicePayment.getCurrency();
}
invoiceApi.recordChargeback(paymentControlContext.getPaymentId(), paymentControlContext.getTransactionExternalKey(), amount, currency, internalContext);
}
break;
case CREDIT:
final Map<UUID, BigDecimal> idWithAmountMap = extractIdsWithAmountFromProperties(pluginProperties);
final PluginProperty properties = getPluginProperty(pluginProperties, PROP_IPCD_REFUND_WITH_ADJUSTMENTS);
final boolean isInvoiceAdjusted = properties != null ? Boolean.valueOf((String) properties.getValue()) : false;
final PluginProperty legacyPayment = getPluginProperty(pluginProperties, PROP_IPCD_PAYMENT_ID);
final UUID paymentId = legacyPayment != null ? (UUID) legacyPayment.getValue() : paymentControlContext.getPaymentId();
invoiceApi.recordRefund(paymentId, paymentControlContext.getAmount(), isInvoiceAdjusted, idWithAmountMap, paymentControlContext.getTransactionExternalKey(), internalContext);
break;
default:
throw new IllegalStateException("Unexpected transactionType " + transactionType);
}
} catch (final InvoiceApiException e) {
log.warn("onSuccessCall failed for attemptId='{}', transactionType='{}'", paymentControlContext.getAttemptPaymentId(), transactionType, e);
}
return new DefaultOnSuccessPaymentControlResult();
}
use of org.killbill.billing.payment.api.PluginProperty in project killbill by killbill.
the class TestIntegrationBase method cancelEntitlementAndCheckForCompletion.
protected DefaultEntitlement cancelEntitlementAndCheckForCompletion(final Entitlement entitlement, final LocalDate requestedDate, final NextEvent... events) {
return (DefaultEntitlement) doCallAndCheckForCompletion(new Function<Void, Entitlement>() {
@Override
public Entitlement apply(@Nullable final Void dontcare) {
try {
// Need to fetch again to get latest CTD updated from the system
Entitlement refreshedEntitlement = entitlementApi.getEntitlementForId(entitlement.getId(), callContext);
refreshedEntitlement = refreshedEntitlement.cancelEntitlementWithDate(requestedDate, false, ImmutableList.<PluginProperty>of(), callContext);
return refreshedEntitlement;
} catch (final EntitlementApiException e) {
fail(e.getMessage());
return null;
}
}
}, events);
}
Aggregations