@Override
  public BeginCheckoutResponse beginCheckout(
      final Payment payment, final String successUrl, final String cancelUrl)
      throws FrameworkException {

    final List<PaymentDetailsType> paymentDetailList = new LinkedList<>();
    final List<PaymentDetailsItemType> lineItems = new LinkedList<>();
    final PaymentDetailsType paymentDetails = new PaymentDetailsType();

    for (final PaymentItem item : payment.getItems()) {

      // create payment item
      final PaymentDetailsItemType paymentDetailsItem = new PaymentDetailsItemType();

      paymentDetailsItem.setAmount(
          PayPalHelper.getAmountForCurrency(payment.getCurrencyCode(), item.getAmount()));
      paymentDetailsItem.setQuantity(item.getQuantity());

      final String name = item.getName();
      if (name != null) {

        paymentDetailsItem.setName(name);
      }

      final String description = item.getDescription();
      if (description != null) {

        paymentDetailsItem.setDescription(description);
      }

      final String itemUrl = item.getItemUrl();
      if (itemUrl != null) {

        paymentDetailsItem.setItemURL(itemUrl);
      }

      final String itemNumber = item.getItemNumber();
      if (itemNumber != null) {

        paymentDetailsItem.setNumber(itemNumber);
      }

      lineItems.add(paymentDetailsItem);
    }

    paymentDetails.setPaymentAction(PaymentActionCodeType.SALE);
    paymentDetails.setPaymentDetailsItem(lineItems);
    paymentDetails.setOrderTotal(
        PayPalHelper.getAmountForCurrency(payment.getCurrencyCode(), payment.getTotal()));

    paymentDetailList.add(paymentDetails);

    try {
      final SetExpressCheckoutResponseType response =
          PayPalHelper.getExpressCheckoutToken(paymentDetailList, successUrl, cancelUrl);
      if (AckCodeType.SUCCESS.equals(response.getAck())) {

        payment.setToken(response.getToken());
        payment.setPaymentState(PaymentState.open);
        return new PayPalBeginCheckoutResponse(response, response.getToken());
      }

    } catch (Throwable t) {

      throw new FrameworkException(422, t.getMessage());
    }

    throw new FrameworkException(422, "Unknown error.");
  }
  @Override
  public ConfirmCheckoutResponse confirmCheckout(
      final Payment payment, final String notifyUrl, final String token, final String payerId)
      throws FrameworkException {

    try {

      final GetExpressCheckoutDetailsResponseType response =
          PayPalHelper.getExpressCheckoutResponse(token);

      if (AckCodeType.SUCCESS.equals(response.getAck())) {

        // TODO: change currency code
        final GetExpressCheckoutDetailsResponseDetailsType details =
            response.getGetExpressCheckoutDetailsResponseDetails();
        final CurrencyCodeType currencyCode = CurrencyCodeType.fromValue(payment.getCurrencyCode());

        final DoExpressCheckoutPaymentResponseType confirmationResponse =
            PayPalHelper.commitExpressCheckout(
                notifyUrl, currencyCode, payment.getTotal(), token, payerId);

        if (AckCodeType.SUCCESS.equals(confirmationResponse.getAck())) {

          final PayPalConfirmCheckoutResponse checkoutResponse =
              new PayPalConfirmCheckoutResponse(confirmationResponse);
          final DoExpressCheckoutPaymentResponseDetailsType confirmation =
              confirmationResponse.getDoExpressCheckoutPaymentResponseDetails();
          final String billingAgreementId = confirmation.getBillingAgreementID();
          final String note = confirmation.getNote();

          // billing address
          final AddressType billingAddress = details.getBillingAddress();
          if (billingAddress != null) {

            final String billingAddressName = billingAddress.getName();
            final String billingAddressStreet1 = billingAddress.getStreet1();
            final String billingAddressStreet2 = billingAddress.getStreet2();
            final String billingAddressZip = billingAddress.getPostalCode();
            final String billingAddressCity = billingAddress.getCityName();
            final String billingAddressCountry = billingAddress.getCountryName();

            payment.setBillingAddressName(billingAddressName);
            payment.setBillingAddressStreet1(billingAddressStreet1);
            payment.setBillingAddressStreet2(billingAddressStreet2);
            payment.setBillingAddressZip(billingAddressZip);
            payment.setBillingAddressCity(billingAddressCity);
            payment.setBillingAddressCountry(billingAddressCountry);
          }

          // payer info
          final PayerInfoType payerInfo = details.getPayerInfo();
          if (payerInfo != null) {

            payment.setPayer(payerInfo.getPayer());
            payment.setPayerBusiness(payerInfo.getPayerBusiness());

            final AddressType payerAddress = payerInfo.getAddress();
            if (payerAddress != null) {

              payment.setPayerAddressName(payerAddress.getName());
              payment.setPayerAddressStreet1(payerAddress.getStreet1());
              payment.setPayerAddressStreet2(payerAddress.getStreet1());
              payment.setPayerAddressZip(payerAddress.getPostalCode());
              payment.setPayerAddressCity(payerAddress.getCityName());
              payment.setPayerAddressCountry(payerAddress.getCountryName());
            }
          }

          payment.setBillingAgreementId(billingAgreementId);
          payment.setNote(note);
          payment.setInvoiceId(details.getInvoiceID());
          payment.setPaymentState(PaymentState.completed);
          payment.setToken(null);

          // success
          return checkoutResponse;
        }
      }

    } catch (Throwable t) {
      throw new FrameworkException(422, t.getMessage());
    }

    throw new FrameworkException(422, "Unknown error.");
  }