use of org.killbill.billing.payment.api.PaymentMethod 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.api.PaymentMethod in project killbill by killbill.
the class ComboPaymentResource method getOrCreatePaymentMethod.
protected UUID getOrCreatePaymentMethod(final Account account, @Nullable final PaymentMethodJson paymentMethodJson, final Iterable<PluginProperty> pluginProperties, final CallContext callContext) throws PaymentApiException {
// No info about payment method was passed, we default to null payment Method ID (which is allowed to be overridden in payment control plugins)
if (paymentMethodJson == null || paymentMethodJson.getPluginName() == null) {
return null;
}
// Get all payment methods for account
final List<PaymentMethod> accountPaymentMethods = paymentApi.getAccountPaymentMethods(account.getId(), false, ImmutableList.<PluginProperty>of(), callContext);
// If we were specified a paymentMethod id and we find it, we return it
if (paymentMethodJson.getPaymentMethodId() != null) {
final UUID match = UUID.fromString(paymentMethodJson.getPaymentMethodId());
if (Iterables.any(accountPaymentMethods, new Predicate<PaymentMethod>() {
@Override
public boolean apply(final PaymentMethod input) {
return input.getId().equals(match);
}
})) {
return match;
}
throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT_METHOD, match);
}
// If we were specified a paymentMethod externalKey and we find it, we return it
if (paymentMethodJson.getExternalKey() != null) {
final PaymentMethod match = Iterables.tryFind(accountPaymentMethods, new Predicate<PaymentMethod>() {
@Override
public boolean apply(final PaymentMethod input) {
return input.getExternalKey().equals(paymentMethodJson.getExternalKey());
}
}).orNull();
if (match != null) {
return match.getId();
}
}
// Only set as default if this is the first paymentMethod on the account
final boolean isDefault = accountPaymentMethods.isEmpty();
final PaymentMethod paymentData = paymentMethodJson.toPaymentMethod(account.getId().toString());
return paymentApi.addPaymentMethod(account, paymentMethodJson.getExternalKey(), paymentMethodJson.getPluginName(), isDefault, paymentData.getPluginDetail(), pluginProperties, callContext);
}
use of org.killbill.billing.payment.api.PaymentMethod in project killbill by killbill.
the class PaymentMethodResource method deletePaymentMethod.
@TimedResource
@DELETE
@Produces(APPLICATION_JSON)
@Path("/{paymentMethodId:" + UUID_PATTERN + "}")
@ApiOperation(value = "Delete a payment method")
@ApiResponses(value = { @ApiResponse(code = 400, message = "Invalid paymentMethodId supplied"), @ApiResponse(code = 404, message = "Account or payment method not found") })
public Response deletePaymentMethod(@PathParam("paymentMethodId") final String paymentMethodId, @QueryParam(QUERY_DELETE_DEFAULT_PM_WITH_AUTO_PAY_OFF) @DefaultValue("false") final Boolean deleteDefaultPaymentMethodWithAutoPayOff, @QueryParam(QUERY_FORCE_DEFAULT_PM_DELETION) @DefaultValue("false") final Boolean forceDefaultPaymentMethodDeletion, @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString, @HeaderParam(HDR_CREATED_BY) final String createdBy, @HeaderParam(HDR_REASON) final String reason, @HeaderParam(HDR_COMMENT) final String comment, @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException, AccountApiException {
final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
final CallContext callContext = context.createContext(createdBy, reason, comment, request);
final PaymentMethod paymentMethod = paymentApi.getPaymentMethodById(UUID.fromString(paymentMethodId), false, false, pluginProperties, callContext);
final Account account = accountUserApi.getAccountById(paymentMethod.getAccountId(), callContext);
paymentApi.deletePaymentMethod(account, UUID.fromString(paymentMethodId), deleteDefaultPaymentMethodWithAutoPayOff, forceDefaultPaymentMethodDeletion, pluginProperties, callContext);
return Response.status(Status.OK).build();
}
use of org.killbill.billing.payment.api.PaymentMethod in project killbill by killbill.
the class PaymentMethodResource method searchPaymentMethods.
@TimedResource
@GET
@Path("/" + SEARCH + "/{searchKey:" + ANYTHING_PATTERN + "}")
@Produces(APPLICATION_JSON)
@ApiOperation(value = "Search payment methods", response = PaymentMethodJson.class, responseContainer = "List")
@ApiResponses(value = {})
public Response searchPaymentMethods(@PathParam("searchKey") final String searchKey, @QueryParam(QUERY_SEARCH_OFFSET) @DefaultValue("0") final Long offset, @QueryParam(QUERY_SEARCH_LIMIT) @DefaultValue("100") final Long limit, @QueryParam(QUERY_PAYMENT_METHOD_PLUGIN_NAME) final String pluginName, @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString, @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode, @QueryParam(QUERY_WITH_PLUGIN_INFO) @DefaultValue("false") final Boolean withPluginInfo, @javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException, AccountApiException {
final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
final TenantContext tenantContext = context.createContext(request);
// Search the plugin(s)
final Pagination<PaymentMethod> paymentMethods;
if (Strings.isNullOrEmpty(pluginName)) {
paymentMethods = paymentApi.searchPaymentMethods(searchKey, offset, limit, withPluginInfo, pluginProperties, tenantContext);
} else {
paymentMethods = paymentApi.searchPaymentMethods(searchKey, offset, limit, pluginName, withPluginInfo, pluginProperties, tenantContext);
}
final URI nextPageUri = uriBuilder.nextPage(PaymentMethodResource.class, "searchPaymentMethods", paymentMethods.getNextOffset(), limit, ImmutableMap.<String, String>of("searchKey", searchKey, QUERY_PAYMENT_METHOD_PLUGIN_NAME, Strings.nullToEmpty(pluginName), QUERY_AUDIT, auditMode.getLevel().toString()));
final AtomicReference<Map<UUID, AccountAuditLogs>> accountsAuditLogs = new AtomicReference<Map<UUID, AccountAuditLogs>>(new HashMap<UUID, AccountAuditLogs>());
final Map<UUID, Account> accounts = new HashMap<UUID, Account>();
return buildStreamingPaginationResponse(paymentMethods, new Function<PaymentMethod, PaymentMethodJson>() {
@Override
public PaymentMethodJson apply(final PaymentMethod paymentMethod) {
// Cache audit logs per account
if (accountsAuditLogs.get().get(paymentMethod.getAccountId()) == null) {
accountsAuditLogs.get().put(paymentMethod.getAccountId(), auditUserApi.getAccountAuditLogs(paymentMethod.getAccountId(), auditMode.getLevel(), tenantContext));
}
// Lookup the associated account(s)
if (accounts.get(paymentMethod.getAccountId()) == null) {
final Account account;
try {
account = accountUserApi.getAccountById(paymentMethod.getAccountId(), tenantContext);
accounts.put(paymentMethod.getAccountId(), account);
} catch (final AccountApiException e) {
log.warn("Error retrieving accountId='{}'", paymentMethod.getAccountId(), e);
return null;
}
}
return PaymentMethodJson.toPaymentMethodJson(accounts.get(paymentMethod.getAccountId()), paymentMethod, accountsAuditLogs.get().get(paymentMethod.getAccountId()));
}
}, nextPageUri);
}
use of org.killbill.billing.payment.api.PaymentMethod in project killbill by killbill.
the class PaymentMethodResource method getPaymentMethod.
@TimedResource(name = "getPaymentMethod")
@GET
@Path("/{paymentMethodId:" + UUID_PATTERN + "}")
@Produces(APPLICATION_JSON)
@ApiOperation(value = "Retrieve a payment method by id", response = PaymentMethodJson.class)
@ApiResponses(value = { @ApiResponse(code = 400, message = "Invalid paymentMethodId supplied"), @ApiResponse(code = 404, message = "Account or payment method not found") })
public Response getPaymentMethod(@PathParam("paymentMethodId") final String paymentMethodId, @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString, @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode, @QueryParam(QUERY_WITH_PLUGIN_INFO) @DefaultValue("false") final Boolean withPluginInfo, @javax.ws.rs.core.Context final HttpServletRequest request) throws AccountApiException, PaymentApiException {
final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
final TenantContext tenantContext = context.createContext(request);
final PaymentMethod paymentMethod = paymentApi.getPaymentMethodById(UUID.fromString(paymentMethodId), false, withPluginInfo, pluginProperties, tenantContext);
final Account account = accountUserApi.getAccountById(paymentMethod.getAccountId(), tenantContext);
final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(paymentMethod.getAccountId(), auditMode.getLevel(), tenantContext);
final PaymentMethodJson json = PaymentMethodJson.toPaymentMethodJson(account, paymentMethod, accountAuditLogs);
return Response.status(Status.OK).entity(json).build();
}
Aggregations