use of org.killbill.billing.payment.dao.PaymentMethodModelDao in project killbill by killbill.
the class PaymentMethodProcessor method refreshPaymentMethods.
/**
* This refreshed the payment methods from the plugin for cases when adding payment method does not flow through KB because of PCI compliance
* issues. The logic below is not optimal because there is no atomicity in the step but the good news is that this is idempotent so can always be
* replayed if necessary-- partial failure scenario.
*
* @param pluginName
* @param account
* @param context
* @return the list of payment methods -- should be identical between KB, the plugin view-- if it keeps a state-- and the gateway.
* @throws PaymentApiException
*/
public List<PaymentMethod> refreshPaymentMethods(final String pluginName, final Account account, final Iterable<PluginProperty> properties, final CallContext callContext, final InternalCallContext context) throws PaymentApiException {
// Don't hold the account lock while fetching the payment methods from the gateway as those could change anyway
final PaymentPluginApi pluginApi = getPaymentPluginApi(pluginName);
final List<PaymentMethodInfoPlugin> pluginPms;
try {
pluginPms = pluginApi.getPaymentMethods(account.getId(), true, properties, callContext);
// The method should never return null by convention, but let's not trust the plugin...
if (pluginPms == null) {
log.debug("No payment methods defined on the account {} for plugin {}", account.getId(), pluginName);
return ImmutableList.<PaymentMethod>of();
}
} catch (final PaymentPluginApiException e) {
throw new PaymentApiException(ErrorCode.PAYMENT_REFRESH_PAYMENT_METHOD, account.getId(), e.getErrorMessage());
}
try {
final PluginDispatcherReturnType<List<PaymentMethod>> result = new WithAccountLock<List<PaymentMethod>, PaymentApiException>(paymentConfig).processAccountWithLock(locker, account.getId(), new DispatcherCallback<PluginDispatcherReturnType<List<PaymentMethod>>, PaymentApiException>() {
@Override
public PluginDispatcherReturnType<List<PaymentMethod>> doOperation() throws PaymentApiException {
UUID defaultPaymentMethodId = null;
final List<PaymentMethodInfoPlugin> pluginPmsWithId = new ArrayList<PaymentMethodInfoPlugin>();
final List<PaymentMethodModelDao> finalPaymentMethods = new ArrayList<PaymentMethodModelDao>();
for (final PaymentMethodInfoPlugin cur : pluginPms) {
// If the kbPaymentId is NULL, the plugin does not know about it, so we create a new UUID
final UUID paymentMethodId = cur.getPaymentMethodId() != null ? cur.getPaymentMethodId() : UUIDs.randomUUID();
// TODO paymentMethod externalKey seems broken here.
final PaymentMethod input = new DefaultPaymentMethod(paymentMethodId, paymentMethodId.toString(), account.getId(), pluginName);
final PaymentMethodModelDao pmModel = new PaymentMethodModelDao(input.getId(), input.getExternalKey(), input.getCreatedDate(), input.getUpdatedDate(), input.getAccountId(), input.getPluginName(), input.isActive());
finalPaymentMethods.add(pmModel);
pluginPmsWithId.add(new DefaultPaymentMethodInfoPlugin(cur, paymentMethodId));
// will always return false - it's Kill Bill in that case which is responsible to manage default payment methods
if (cur.isDefault()) {
defaultPaymentMethodId = paymentMethodId;
}
}
final List<PaymentMethodModelDao> refreshedPaymentMethods = paymentDao.refreshPaymentMethods(pluginName, finalPaymentMethods, context);
try {
pluginApi.resetPaymentMethods(account.getId(), pluginPmsWithId, properties, callContext);
} catch (final PaymentPluginApiException e) {
throw new PaymentApiException(ErrorCode.PAYMENT_REFRESH_PAYMENT_METHOD, account.getId(), e.getErrorMessage());
}
try {
updateDefaultPaymentMethodIfNeeded(pluginName, account, defaultPaymentMethodId, context);
} catch (final AccountApiException e) {
throw new PaymentApiException(e);
}
final List<PaymentMethod> result = ImmutableList.<PaymentMethod>copyOf(Collections2.transform(refreshedPaymentMethods, new Function<PaymentMethodModelDao, PaymentMethod>() {
@Override
public PaymentMethod apply(final PaymentMethodModelDao input) {
return new DefaultPaymentMethod(input, null);
}
}));
return PluginDispatcher.createPluginDispatcherReturnType(result);
}
});
return result.getReturnType();
} catch (final Exception e) {
throw new PaymentApiException(e, ErrorCode.PAYMENT_INTERNAL_ERROR, MoreObjects.firstNonNull(e.getMessage(), ""));
}
}
use of org.killbill.billing.payment.dao.PaymentMethodModelDao in project killbill by killbill.
the class TestPaymentMethodProcessorRefreshWithDB method testRefreshWithDeletedPaymentMethod.
@Test(groups = "slow")
public void testRefreshWithDeletedPaymentMethod() throws Exception {
final Account account = testHelper.createTestAccount("super@bar.com", true);
Assert.assertEquals(getPluginApi().getPaymentMethods(account.getId(), true, PLUGIN_PROPERTIES, callContext).size(), 1);
final UUID firstPmId = account.getPaymentMethodId();
String secondPaymentMethodExternalKey = UUID.randomUUID().toString();
final UUID secondPmId = paymentApi.addPaymentMethod(account, secondPaymentMethodExternalKey, MockPaymentProviderPlugin.PLUGIN_NAME, true, new DefaultNoOpPaymentMethodPlugin(secondPaymentMethodExternalKey, false, null), PLUGIN_PROPERTIES, callContext);
Assert.assertEquals(getPluginApi().getPaymentMethods(account.getId(), true, PLUGIN_PROPERTIES, callContext).size(), 2);
Assert.assertEquals(paymentApi.getAccountPaymentMethods(account.getId(), false, PLUGIN_PROPERTIES, callContext).size(), 2);
// Remove second PM from plugin
getPluginApi().deletePaymentMethod(account.getId(), secondPmId, PLUGIN_PROPERTIES, callContext);
Assert.assertEquals(getPluginApi().getPaymentMethods(account.getId(), true, PLUGIN_PROPERTIES, callContext).size(), 1);
Assert.assertEquals(paymentApi.getAccountPaymentMethods(account.getId(), false, PLUGIN_PROPERTIES, callContext).size(), 2);
// Verify that the refresh sees that PM as being deleted now
final List<PaymentMethod> methods = paymentMethodProcessor.refreshPaymentMethods(MockPaymentProviderPlugin.PLUGIN_NAME, account, PLUGIN_PROPERTIES, callContext, internalCallContext);
Assert.assertEquals(methods.size(), 1);
checkPaymentMethodExistsWithStatus(methods, firstPmId, true);
final PaymentMethodModelDao deletedPMModel = paymentDao.getPaymentMethodIncludedDeleted(secondPmId, internalCallContext);
Assert.assertNotNull(deletedPMModel);
Assert.assertFalse(deletedPMModel.isActive());
}
use of org.killbill.billing.payment.dao.PaymentMethodModelDao in project killbill by killbill.
the class PaymentAutomatonDAOHelper method getPaymentProviderPluginName.
public String getPaymentProviderPluginName(final boolean includeDeleted) throws PaymentApiException {
if (pluginName != null) {
return pluginName;
}
final PaymentMethodModelDao methodDao = paymentPluginServiceRegistration.getPaymentMethodById(paymentStateContext.getPaymentMethodId(), includeDeleted, internalCallContext);
pluginName = methodDao.getPluginName();
return pluginName;
}
use of org.killbill.billing.payment.dao.PaymentMethodModelDao in project killbill by killbill.
the class PaymentMethodProcessor method updateDefaultPaymentMethodIfNeeded.
private void updateDefaultPaymentMethodIfNeeded(final String pluginName, final Account account, @Nullable final UUID defaultPluginPaymentMethodId, final InternalCallContext context) throws PaymentApiException, AccountApiException {
// If the plugin does not have a default payment gateway, we keep the current default payment method in KB account as it is.
if (defaultPluginPaymentMethodId == null) {
return;
}
// Some gateways have the concept of default payment methods. Kill Bill has also its own default payment method
// and is authoritative on this matter. However, if the default payment method is associated with a given plugin,
// and if the default payment method in that plugin has changed, we will reflect this change in Kill Bill as well.
boolean shouldUpdateDefaultPaymentMethod = true;
if (account.getPaymentMethodId() != null) {
final PaymentMethodModelDao currentDefaultPaymentMethod = getPaymentMethodById(account.getPaymentMethodId(), false, context);
shouldUpdateDefaultPaymentMethod = pluginName.equals(currentDefaultPaymentMethod.getPluginName());
}
if (shouldUpdateDefaultPaymentMethod) {
accountInternalApi.updatePaymentMethod(account.getId(), defaultPluginPaymentMethodId, context);
}
}
use of org.killbill.billing.payment.dao.PaymentMethodModelDao in project killbill by killbill.
the class PaymentMethodProcessor method addPaymentMethod.
public UUID addPaymentMethod(final String paymentMethodExternalKey, final String paymentPluginServiceName, final Account account, final boolean setDefault, final PaymentMethodPlugin paymentMethodProps, final Iterable<PluginProperty> properties, final CallContext callContext, final InternalCallContext context) throws PaymentApiException {
return dispatchWithExceptionHandling(account, paymentPluginServiceName, new CallableWithAccountLock<UUID, PaymentApiException>(locker, account.getId(), paymentConfig, new DispatcherCallback<PluginDispatcherReturnType<UUID>, PaymentApiException>() {
@Override
public PluginDispatcherReturnType<UUID> doOperation() throws PaymentApiException {
PaymentMethod pm = null;
try {
validateUniqueExternalPaymentMethod(account.getId(), paymentPluginServiceName);
pm = new DefaultPaymentMethod(paymentMethodExternalKey, account.getId(), paymentPluginServiceName, paymentMethodProps);
final PaymentPluginApi pluginApi = getPaymentPluginApi(paymentPluginServiceName);
pluginApi.addPaymentMethod(account.getId(), pm.getId(), paymentMethodProps, setDefault, properties, callContext);
final String actualPaymentMethodExternalKey = retrieveActualPaymentMethodExternalKey(account, pm, pluginApi, properties, callContext, context);
final PaymentMethodModelDao pmModel = new PaymentMethodModelDao(pm.getId(), actualPaymentMethodExternalKey, pm.getCreatedDate(), pm.getUpdatedDate(), pm.getAccountId(), pm.getPluginName(), pm.isActive());
paymentDao.insertPaymentMethod(pmModel, context);
if (setDefault) {
accountInternalApi.updatePaymentMethod(account.getId(), pm.getId(), context);
}
} catch (final PaymentPluginApiException e) {
throw new PaymentApiException(ErrorCode.PAYMENT_ADD_PAYMENT_METHOD, account.getId(), e.getErrorMessage());
} catch (final AccountApiException e) {
throw new PaymentApiException(e);
}
return PluginDispatcher.createPluginDispatcherReturnType(pm.getId());
}
private void validateUniqueExternalPaymentMethod(final UUID accountId, final String pluginName) throws PaymentApiException {
if (ExternalPaymentProviderPlugin.PLUGIN_NAME.equals(pluginName)) {
final List<PaymentMethodModelDao> accountPaymentMethods = paymentDao.getPaymentMethods(context);
if (Iterables.any(accountPaymentMethods, new Predicate<PaymentMethodModelDao>() {
@Override
public boolean apply(final PaymentMethodModelDao input) {
return ExternalPaymentProviderPlugin.PLUGIN_NAME.equals(input.getPluginName());
}
})) {
throw new PaymentApiException(ErrorCode.PAYMENT_EXTERNAL_PAYMENT_METHOD_ALREADY_EXISTS, accountId);
}
}
}
}), uuidPluginNotificationDispatcher);
}
Aggregations