use of alfio.model.PurchaseContext in project alf.io by alfio-event.
the class BankTransferManager method getModelOptions.
@Override
public Map<String, ?> getModelOptions(PaymentContext context) {
OptionalInt delay = getOfflinePaymentWaitingPeriod(context, configurationManager);
PurchaseContext purchaseContext = context.getPurchaseContext();
if (delay.isEmpty()) {
log.error("Already started event {} has been found with OFFLINE payment enabled", purchaseContext.getDisplayName());
}
Map<String, Object> model = new HashMap<>();
model.put("delayForOfflinePayment", Math.max(1, delay.orElse(0)));
boolean recaptchaEnabled = configurationManager.isRecaptchaForOfflinePaymentAndFreeEnabled(purchaseContext.getConfigurationLevel());
model.put("captchaRequestedForOffline", recaptchaEnabled);
if (recaptchaEnabled) {
model.put("recaptchaApiKey", configurationManager.getForSystem(RECAPTCHA_API_KEY).getValue().orElse(null));
}
return model;
}
use of alfio.model.PurchaseContext in project alf.io by alfio-event.
the class StripeWebhookPaymentManager method forceTransactionCheck.
@Override
public PaymentWebhookResult forceTransactionCheck(TicketReservation reservation, Transaction transaction, PaymentContext paymentContext) {
Validate.isTrue(transaction.getPaymentProxy() == PaymentProxy.STRIPE, "invalid transaction");
try {
PurchaseContext purchaseContext = paymentContext.getPurchaseContext();
var options = baseStripeManager.options(purchaseContext, builder -> builder.setIdempotencyKey(reservation.getId())).orElseThrow();
var intent = PaymentIntent.retrieve(transaction.getPaymentId(), options);
switch(intent.getStatus()) {
case "processing":
case "requires_action":
case "requires_confirmation":
return PaymentWebhookResult.pending();
case "succeeded":
return processSuccessfulPaymentIntent(transaction, intent, reservation, purchaseContext);
case REQUIRES_PAYMENT_METHOD:
// payment is failed.
return processFailedPaymentIntent(transaction, reservation, purchaseContext);
default:
return null;
}
} catch (Exception ex) {
log.error("Error trying to check PaymentIntent status", ex);
return PaymentWebhookResult.error("failed");
}
}
use of alfio.model.PurchaseContext in project alf.io by alfio-event.
the class BaseStripeManager method refund.
// https://stripe.com/docs/api#create_refund
boolean refund(Transaction transaction, PurchaseContext purchaseContext, Integer amountToRefund) {
Optional<Integer> amount = Optional.ofNullable(amountToRefund);
String chargeId = transaction.getTransactionId();
try {
String amountOrFull = amount.map(p -> MonetaryUtil.formatCents(p, transaction.getCurrency())).orElse("full");
log.info("Stripe: trying to do a refund for payment {} with amount: {}", chargeId, amountOrFull);
Map<String, Object> params = new HashMap<>();
params.put("charge", chargeId);
amount.ifPresent(a -> params.put("amount", a));
if (transaction.getPlatformFee() > 0 && isConnectEnabled(new PaymentContext(purchaseContext))) {
params.put("refund_application_fee", true);
}
Optional<RequestOptions> requestOptionsOptional = options(purchaseContext);
if (requestOptionsOptional.isPresent()) {
RequestOptions options = requestOptionsOptional.get();
Refund r = Refund.create(params, options);
boolean pending = PENDING.equals(r.getStatus());
if (SUCCEEDED.equals(r.getStatus()) || pending) {
log.info("Stripe: refund for payment {} {} for amount: {}", chargeId, pending ? "registered" : "executed with success", amountOrFull);
return true;
} else {
log.warn("Stripe: was not able to refund payment with id {}, returned status is not 'succeded' but {}", chargeId, r.getStatus());
return false;
}
}
return false;
} catch (StripeException e) {
log.warn("Stripe: was not able to refund payment with id " + chargeId, e);
return false;
}
}
use of alfio.model.PurchaseContext in project alf.io by alfio-event.
the class MollieWebhookPaymentManager method initPayment.
private PaymentResult initPayment(TicketReservation reservation, PaymentSpecification spec, String baseUrl, Map<ConfigurationKeys, ConfigurationManager.MaybeConfiguration> configuration) throws IOException, InterruptedException {
var purchaseContext = spec.getPurchaseContext();
var purchaseContextUrlComponent = purchaseContext.getType().getUrlComponent();
var publicIdentifier = purchaseContext.getPublicIdentifier();
var reservationId = reservation.getId();
String bookUrl = baseUrl + "/" + purchaseContextUrlComponent + "/" + publicIdentifier + "/reservation/" + reservationId + "/book";
final int items;
if (spec.getPurchaseContext().getType() == PurchaseContext.PurchaseContextType.event) {
items = ticketRepository.countTicketsInReservation(spec.getReservationId());
} else {
items = 1;
}
Map<String, Object> payload = new HashMap<>();
payload.put(AMOUNT, Map.of(VALUE, spec.getOrderSummary().getTotalPrice(), CURRENCY, spec.getPurchaseContext().getCurrency()));
var description = purchaseContext.ofType(PurchaseContext.PurchaseContextType.event) ? "ticket(s) for event" : "x subscription";
payload.put("description", String.format("%s - %d %s %s", configurationManager.getShortReservationID(spec.getPurchaseContext(), reservation), items, description, spec.getPurchaseContext().getDisplayName()));
payload.put("redirectUrl", bookUrl);
payload.put("webhookUrl", baseUrl + UriComponentsBuilder.fromPath(WEBHOOK_URL_TEMPLATE).buildAndExpand(reservationId).toUriString());
payload.put("metadata", MetadataBuilder.buildMetadata(spec, Map.of()));
if (configuration.get(PLATFORM_MODE_ENABLED).getValueAsBooleanOrDefault()) {
payload.put("profileId", configuration.get(MOLLIE_CONNECT_PROFILE_ID).getRequiredValue());
payload.put("testmode", !configuration.get(MOLLIE_CONNECT_LIVE_MODE).getValueAsBooleanOrDefault());
String currencyCode = spec.getCurrencyCode();
FeeCalculator.getCalculator(spec.getPurchaseContext(), configurationManager, currencyCode).apply(items, (long) spec.getPriceWithVAT()).filter(// minimum fee for Mollie is 0.01
fee -> fee > 1L).map(fee -> MonetaryUtil.formatCents(fee, currencyCode)).ifPresent(fee -> payload.put("applicationFee", Map.of(AMOUNT, Map.of(CURRENCY, currencyCode, VALUE, fee), "description", "Reservation" + reservationId)));
}
HttpRequest request = requestFor(PAYMENTS_ENDPOINT, configuration, spec.getPurchaseContext().getConfigurationLevel()).header(HttpUtils.CONTENT_TYPE, HttpUtils.APPLICATION_JSON).POST(HttpRequest.BodyPublishers.ofString(Json.GSON.toJson(payload))).build();
HttpResponse<InputStream> response = client.send(request, HttpResponse.BodyHandlers.ofInputStream());
if (HttpUtils.callSuccessful(response)) {
try (var responseReader = new InputStreamReader(response.body(), UTF_8)) {
var body = new MolliePaymentDetails(JsonParser.parseReader(responseReader).getAsJsonObject());
var paymentId = body.getPaymentId();
var checkoutLink = body.getCheckoutLink();
// we give an additional slack to process the payment
var expiration = body.getExpiresAt().orElseThrow().plusMinutes(5);
ticketReservationRepository.updateReservationStatus(reservationId, EXTERNAL_PROCESSING_PAYMENT.toString());
ticketReservationRepository.updateValidity(reservationId, Date.from(expiration.toInstant()));
invalidateExistingTransactions(reservationId, transactionRepository);
transactionRepository.insert(paymentId, paymentId, reservationId, ZonedDateTime.now(clockProvider.withZone(spec.getPurchaseContext().getZoneId())), spec.getPriceWithVAT(), spec.getPurchaseContext().getCurrency(), "Mollie Payment", PaymentProxy.MOLLIE.name(), 0L, 0L, Transaction.Status.PENDING, Map.of());
return PaymentResult.redirect(checkoutLink);
}
} else {
log.warn("was not able to create a payment for reservation id " + reservationId);
return PaymentResult.failed(ErrorsCode.STEP_2_PAYMENT_REQUEST_CREATION);
}
}
use of alfio.model.PurchaseContext in project alf.io by alfio-event.
the class BankTransferManager method getOfflinePaymentDeadline.
public static ZonedDateTime getOfflinePaymentDeadline(PaymentContext context, ConfigurationManager configurationManager) {
PurchaseContext purchaseContext = context.getPurchaseContext();
ZonedDateTime now = purchaseContext.now(ClockProvider.clock());
int waitingPeriod = getOfflinePaymentWaitingPeriod(context, configurationManager).orElse(0);
if (waitingPeriod == 0) {
log.warn("accepting offline payments the same day is a very bad practice and should be avoided. Please set cash payment as payment method next time");
// TODO Maybe should we avoid this wrong behavior upfront, in the admin area?
return now.plusHours(2);
}
return now.plusDays(waitingPeriod).truncatedTo(ChronoUnit.HALF_DAYS).with(WorkingDaysAdjusters.defaultWorkingDays());
}
Aggregations