Added Paymill

This commit is contained in:
Hillel Coren 2018-03-07 17:23:25 +02:00
parent 395ce6303b
commit 5c5dd7a27e
18 changed files with 318 additions and 140 deletions

View File

@ -301,6 +301,7 @@ if (! defined('APP_NAME')) {
define('GATEWAY_BRAINTREE', 61);
define('GATEWAY_CUSTOM', 62);
define('GATEWAY_GOCARDLESS', 64);
define('GATEWAY_PAYMILL', 66);
// The customer exists, but only as a local concept
// The remote gateway doesn't understand the concept of customers

View File

@ -473,7 +473,7 @@ class AccountController extends BaseController
$trashedCount = AccountGateway::scope()->withTrashed()->count();
if ($accountGateway = $account->getGatewayConfig(GATEWAY_STRIPE)) {
if (! $accountGateway->getPublishableStripeKey()) {
if (! $accountGateway->getPublishableKey()) {
Session::now('warning', trans('texts.missing_publishable_key'));
}
}

View File

@ -183,6 +183,8 @@ class AccountGatewayController extends BaseController
if ($gatewayId == GATEWAY_DWOLLA) {
$optional = array_merge($optional, ['key', 'secret']);
} elseif ($gatewayId == GATEWAY_PAYMILL) {
$rules['publishable_key'] = 'required';
} elseif ($gatewayId == GATEWAY_STRIPE) {
if (Utils::isNinjaDev()) {
// do nothing - we're unable to acceptance test with StripeJS

View File

@ -71,7 +71,7 @@ class HandleUserLoggedIn
// if they're using Stripe make sure they're using Stripe.js
$accountGateway = $account->getGatewayConfig(GATEWAY_STRIPE);
if ($accountGateway && ! $accountGateway->getPublishableStripeKey()) {
if ($accountGateway && ! $accountGateway->getPublishableKey()) {
Session::flash('warning', trans('texts.missing_publishable_key'));
} elseif ($account->isLogoTooLarge()) {
Session::flash('warning', trans('texts.logo_too_large', ['size' => $account->getLogoSize() . 'KB']));

View File

@ -95,7 +95,16 @@ class AccountGateway extends EntityModel
*/
public function isGateway($gatewayId)
{
return $this->gateway_id == $gatewayId;
if (is_array($gatewayId)) {
foreach ($gatewayId as $id) {
if ($this->gateway_id == $id) {
return true;
}
}
return false;
} else {
return $this->gateway_id == $gatewayId;
}
}
/**
@ -127,9 +136,9 @@ class AccountGateway extends EntityModel
/**
* @return bool|mixed
*/
public function getPublishableStripeKey()
public function getPublishableKey()
{
if (! $this->isGateway(GATEWAY_STRIPE)) {
if (! $this->isGateway([GATEWAY_STRIPE, GATEWAY_PAYMILL])) {
return false;
}
@ -254,7 +263,7 @@ class AccountGateway extends EntityModel
return null;
}
$stripe_key = $this->getPublishableStripeKey();
$stripe_key = $this->getPublishableKey();
return substr(trim($stripe_key), 0, 8) == 'pk_test_' ? 'tartan' : 'production';
}
@ -272,7 +281,7 @@ class AccountGateway extends EntityModel
public function isTestMode()
{
if ($this->isGateway(GATEWAY_STRIPE)) {
return strpos($this->getPublishableStripeKey(), 'test') !== false;
return strpos($this->getPublishableKey(), 'test') !== false;
} else {
return $this->getConfigField('testMode');
}

View File

@ -0,0 +1,27 @@
<?php
namespace App\Ninja\PaymentDrivers;
class PaymillPaymentDriver extends BasePaymentDriver
{
public function tokenize()
{
return true;
}
protected function paymentDetails($paymentMethod = false)
{
$data = parent::paymentDetails($paymentMethod);
if ($paymentMethod) {
return $data;
}
if (! empty($this->input['sourceToken'])) {
$data['token'] = $this->input['sourceToken'];
unset($data['card']);
}
return $data;
}
}

View File

@ -63,7 +63,7 @@ class StripePaymentDriver extends BasePaymentDriver
public function tokenize()
{
return $this->accountGateway->getPublishableStripeKey();
return $this->accountGateway->getPublishableKey();
}
public function rules()

View File

@ -36,7 +36,7 @@
"google/apiclient": "^2.0",
"guzzlehttp/guzzle": "~6.0",
"intervention/image": "dev-master",
"invoiceninja/omnipay-collection": "0.6@dev",
"invoiceninja/omnipay-collection": "0.7@dev",
"jaybizzle/laravel-crawler-detect": "1.*",
"jlapp/swaggervel": "master-dev",
"jonnyw/php-phantomjs": "dev-fixes",

63
composer.lock generated
View File

@ -4,8 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "8c61bb54d84a6fcf49038e56aa706561",
"content-hash": "189d617b1ea403a147b9fe61c0b2185b",
"hash": "7ee707d4d8d5e695a06f09148055a914",
"content-hash": "6d17fba6edb89baff674e8d4a89e00e5",
"packages": [
{
"name": "abdala/omnipay-pagseguro",
@ -838,6 +838,50 @@
"description": "Braintree PHP Client Library",
"time": "2018-02-27 22:51:38"
},
{
"name": "bramdevries/omnipay-paymill",
"version": "1.0.2",
"source": {
"type": "git",
"url": "https://github.com/bramdevries/omnipay-paymill.git",
"reference": "df264a980b4b6005899f659d55b24d5c125d7e2e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/bramdevries/omnipay-paymill/zipball/df264a980b4b6005899f659d55b24d5c125d7e2e",
"reference": "df264a980b4b6005899f659d55b24d5c125d7e2e",
"shasum": ""
},
"require": {
"omnipay/common": "~2.0"
},
"require-dev": {
"omnipay/tests": "~2.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Omnipay\\Paymill\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"authors": [
{
"name": "Bram Devries",
"email": "bramdevries93@gmail.com"
}
],
"description": "Paymill driver for the Omnipay payment processing library",
"keywords": [
"gateway",
"merchant",
"omnipay",
"pay",
"payment",
"paymill"
],
"time": "2014-12-14 17:00:43"
},
{
"name": "cardgate/omnipay-cardgate",
"version": "v2.0.0",
@ -3493,16 +3537,16 @@
},
{
"name": "invoiceninja/omnipay-collection",
"version": "v0.6",
"version": "v0.7",
"source": {
"type": "git",
"url": "https://github.com/invoiceninja/omnipay-collection.git",
"reference": "2ca215e97e3b436b26b0b8f52e865ed94eb61408"
"reference": "12d9c8d40d62301e2226db63d1e5534747f660fe"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/invoiceninja/omnipay-collection/zipball/2ca215e97e3b436b26b0b8f52e865ed94eb61408",
"reference": "2ca215e97e3b436b26b0b8f52e865ed94eb61408",
"url": "https://api.github.com/repos/invoiceninja/omnipay-collection/zipball/12d9c8d40d62301e2226db63d1e5534747f660fe",
"reference": "12d9c8d40d62301e2226db63d1e5534747f660fe",
"shasum": ""
},
"require": {
@ -3510,6 +3554,7 @@
"agmscode/omnipay-agms": "~1.0",
"alfaproject/omnipay-skrill": "dev-master",
"andreas22/omnipay-fasapay": "1.*",
"bramdevries/omnipay-paymill": "^1.0",
"cardgate/omnipay-cardgate": "~2.0",
"delatbabel/omnipay-fatzebra": "dev-master",
"dercoder/omnipay-ecopayz": "~1.0",
@ -3549,7 +3594,7 @@
}
],
"description": "Collection of Omnipay drivers",
"time": "2018-01-17 13:51:24"
"time": "2018-03-07 13:26:16"
},
{
"name": "jakoch/phantomjs-installer",
@ -9243,7 +9288,7 @@
},
{
"name": "symfony/event-dispatcher",
"version": "v2.8.35",
"version": "v2.8.36",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
@ -9401,7 +9446,7 @@
},
{
"name": "symfony/http-foundation",
"version": "v3.4.5",
"version": "v3.4.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",

View File

@ -74,6 +74,7 @@ class PaymentLibrariesSeeder extends Seeder
['name' => 'FirstData Payeezy', 'provider' => 'FirstData_Payeezy'],
['name' => 'GoCardless', 'provider' => 'GoCardlessV2\Redirect', 'sort_order' => 9, 'is_offsite' => true],
['name' => 'PagSeguro', 'provider' => 'PagSeguro'],
['name' => 'PAYMILL', 'provider' => 'Paymill'],
];
foreach ($gateways as $gateway) {

View File

@ -34,7 +34,7 @@
{!! Former::populateField('show_address', intval($accountGateway->show_address)) !!}
{!! Former::populateField('show_shipping_address', intval($accountGateway->show_shipping_address)) !!}
{!! Former::populateField('update_address', intval($accountGateway->update_address)) !!}
{!! Former::populateField('publishable_key', $accountGateway->getPublishableStripeKey() ? str_repeat('*', strlen($accountGateway->getPublishableStripeKey())) : '') !!}
{!! Former::populateField('publishable_key', $accountGateway->getPublishableKey() ? str_repeat('*', strlen($accountGateway->getPublishableKey())) : '') !!}
{!! Former::populateField('enable_ach', $accountGateway->getAchEnabled() ? 1 : 0) !!}
{!! Former::populateField('enable_apple_pay', $accountGateway->getApplePayEnabled() ? 1 : 0) !!}
{!! Former::populateField('enable_sofort', $accountGateway->getSofortEnabled() ? 1 : 0) !!}
@ -84,13 +84,12 @@
@endif
@endif
<span id="publishableKey" style="display: none">
{!! Former::text('publishable_key') !!}
</span>
@foreach ($gateways as $gateway)
<div id="gateway_{{ $gateway->id }}_div" class='gateway-fields' style="display: none">
@if ($gateway->id == GATEWAY_STRIPE)
{!! Former::text('publishable_key') !!}
@endif
@foreach ($gateway->fields as $field => $details)
@if ($details && (!$accountGateway || !$accountGateway->getConfigField($field)) && !is_array($details) && !is_bool($details))
@ -321,6 +320,8 @@
} else {
$('.stripe-ach').hide();
}
$('#publishableKey').toggle([{{ GATEWAY_STRIPE }}, {{ GATEWAY_PAYMILL }}].indexOf(gateway.id) >= 0);
}
function gatewayLink(url) {

View File

@ -118,7 +118,7 @@
<script type="text/javascript" src="https://js.stripe.com/v3/"></script>
<script type="text/javascript">
// https://stripe.com/docs/stripe-js/elements/payment-request-button
var stripe = Stripe('{{ $accountGateway->getPublishableStripeKey() }}');
var stripe = Stripe('{{ $accountGateway->getPublishableKey() }}');
var paymentRequest = stripe.paymentRequest({
country: '{{ $invoice->client->getCountryCode() }}',
currency: '{{ strtolower($invoice->client->getCurrencyCode()) }}',

View File

@ -6,7 +6,7 @@
<script type="text/javascript" src="https://js.stripe.com/v3/"></script>
<script type="text/javascript">
// https://stripe.com/docs/stripe-js/elements/payment-request-button
var stripe = Stripe('{{ $accountGateway->getPublishableStripeKey() }}');
var stripe = Stripe('{{ $accountGateway->getPublishableKey() }}');
var paymentRequest = stripe.paymentRequest({
country: '{{ $client->getCountryCode() }}',
currency: '{{ strtolower($client->getCurrencyCode()) }}',

View File

@ -77,40 +77,41 @@
});
@if ($accountGateway->gateway_id != GATEWAY_BRAINTREE)
var card = new Card({
form: 'form#payment-form', // *required*
container: '.card-wrapper', // *required*
if ($('#card_number').length) {
var card = new Card({
form: 'form#payment-form', // *required*
container: '.card-wrapper', // *required*
formSelectors: {
numberInput: 'input#card_number', // optional — default input[name="number"]
expiryInput: 'input#expiry', // optional — default input[name="expiry"]
cvcInput: 'input#cvv', // optional — default input[name="cvc"]
nameInput: 'input#first_name, input#last_name'
},
formSelectors: {
numberInput: 'input#card_number', // optional — default input[name="number"]
expiryInput: 'input#expiry', // optional — default input[name="expiry"]
cvcInput: 'input#cvv', // optional — default input[name="cvc"]
nameInput: 'input#first_name, input#last_name'
},
//width: 100, // optional — default 350px
formatting: true, // optional - default true
//width: 100, // optional — default 350px
formatting: true, // optional - default true
// Strings for translation - optional
messages: {
monthYear: "{{ trans('texts.month_year') }}",
validDate: "{{ trans('texts.valid_thru') }}",
},
// Strings for translation - optional
messages: {
monthYear: "{{ trans('texts.month_year') }}",
validDate: "{{ trans('texts.valid_thru') }}",
},
// Default placeholders for rendered fields - optional
placeholders: {
number: '•••• •••• •••• ••••',
name: "{{ $client ? ($contact->first_name . ' ' . $contact->last_name) : trans('texts.full_name') }}",
expiry: '••/••',
cvc: '•••'
},
masks: {
cardNumber: '•' // optional - mask card number
},
debug: true,
});
// Default placeholders for rendered fields - optional
placeholders: {
number: '•••• •••• •••• ••••',
name: "{{ $client ? ($contact->first_name . ' ' . $contact->last_name) : trans('texts.full_name') }}",
expiry: '••/••',
cvc: '•••'
},
masks: {
cardNumber: '•' // optional - mask card number
},
debug: true,
});
}
@endif
});
</script>
@ -337,95 +338,103 @@
</h3>
<hr class="form-legend"/>
</div>
<div class="col-lg-{{ ($accountGateway->gateway_id == GATEWAY_BRAINTREE) ? 12 : 8 }}">
<div class="row">
<div class="col-md-12">
@if ($accountGateway->gateway_id == GATEWAY_BRAINTREE)
<div id="card_number" class="braintree-hosted form-control"></div>
@else
{!! Former::text(!empty($tokenize) ? '' : 'card_number')
->id('card_number')
->placeholder(trans('texts.card_number'))
->autocomplete('cc-number')
->label('') !!}
@endif
</div>
@if ($accountGateway->isGateway(GATEWAY_PAYMILL))
<div class="paymill-form">
<div id="paymillCardFields"></div>
<input id="sourceToken" name="sourceToken" type="hidden"/>
</div>
<div class="row">
<div class="col-md-5">
@if ($accountGateway->gateway_id == GATEWAY_BRAINTREE)
<div id="expiration_month" class="braintree-hosted form-control"></div>
@else
{!! Former::select(!empty($tokenize) ? '' : 'expiration_month')
->id('expiration_month')
->autocomplete('cc-exp-month')
->placeholder(trans('texts.expiration_month'))
->addOption('01 - ' . trans('texts.january'), '1')
->addOption('02 - ' . trans('texts.february'), '2')
->addOption('03 - ' . trans('texts.march'), '3')
->addOption('04 - ' . trans('texts.april'), '4')
->addOption('05 - ' . trans('texts.may'), '5')
->addOption('06 - ' . trans('texts.june'), '6')
->addOption('07 - ' . trans('texts.july'), '7')
->addOption('08 - ' . trans('texts.august'), '8')
->addOption('09 - ' . trans('texts.september'), '9')
->addOption('10 - ' . trans('texts.october'), '10')
->addOption('11 - ' . trans('texts.november'), '11')
->addOption('12 - ' . trans('texts.december'), '12')->label('')
!!}
@endif
@else
<div class="col-lg-{{ ($accountGateway->gateway_id == GATEWAY_BRAINTREE) ? 12 : 8 }}">
<div class="row">
<div class="col-md-12">
@if ($accountGateway->gateway_id == GATEWAY_BRAINTREE)
<div id="card_number" class="braintree-hosted form-control"></div>
@else
{!! Former::text(!empty($tokenize) ? '' : 'card_number')
->id('card_number')
->placeholder(trans('texts.card_number'))
->autocomplete('cc-number')
->label('') !!}
@endif
</div>
</div>
<div class="col-md-4">
@if ($accountGateway->gateway_id == GATEWAY_BRAINTREE)
<div id="expiration_year" class="braintree-hosted form-control"></div>
@else
{!! Former::select(!empty($tokenize) ? '' : 'expiration_year')
->id('expiration_year')
->autocomplete('cc-exp-year')
->placeholder(trans('texts.expiration_year'))
->options(
array_combine(
range(date('Y'), date('Y') + 10),
range(date('Y'), date('Y') + 10)
<div class="row">
<div class="col-md-5">
@if ($accountGateway->gateway_id == GATEWAY_BRAINTREE)
<div id="expiration_month" class="braintree-hosted form-control"></div>
@else
{!! Former::select(!empty($tokenize) ? '' : 'expiration_month')
->id('expiration_month')
->autocomplete('cc-exp-month')
->placeholder(trans('texts.expiration_month'))
->addOption('01 - ' . trans('texts.january'), '1')
->addOption('02 - ' . trans('texts.february'), '2')
->addOption('03 - ' . trans('texts.march'), '3')
->addOption('04 - ' . trans('texts.april'), '4')
->addOption('05 - ' . trans('texts.may'), '5')
->addOption('06 - ' . trans('texts.june'), '6')
->addOption('07 - ' . trans('texts.july'), '7')
->addOption('08 - ' . trans('texts.august'), '8')
->addOption('09 - ' . trans('texts.september'), '9')
->addOption('10 - ' . trans('texts.october'), '10')
->addOption('11 - ' . trans('texts.november'), '11')
->addOption('12 - ' . trans('texts.december'), '12')->label('')
!!}
@endif
</div>
<div class="col-md-4">
@if ($accountGateway->gateway_id == GATEWAY_BRAINTREE)
<div id="expiration_year" class="braintree-hosted form-control"></div>
@else
{!! Former::select(!empty($tokenize) ? '' : 'expiration_year')
->id('expiration_year')
->autocomplete('cc-exp-year')
->placeholder(trans('texts.expiration_year'))
->options(
array_combine(
range(date('Y'), date('Y') + 10),
range(date('Y'), date('Y') + 10)
)
)
)
->label('') !!}
@endif
->label('') !!}
@endif
</div>
<div class="col-md-3">
@if ($accountGateway->gateway_id == GATEWAY_BRAINTREE)
<div id="cvv" class="braintree-hosted form-control"></div>
@else
{!! Former::text(!empty($tokenize) ? '' : 'cvv')
->id('cvv')
->placeholder(trans('texts.cvv'))
->autocomplete('off')
->label('') !!}
@endif
</div>
</div>
<div class="col-md-3">
@if ($accountGateway->gateway_id == GATEWAY_BRAINTREE)
<div id="cvv" class="braintree-hosted form-control"></div>
@else
{!! Former::text(!empty($tokenize) ? '' : 'cvv')
->id('cvv')
->placeholder(trans('texts.cvv'))
->autocomplete('off')
->label('') !!}
@endif
<div class="row" style="padding-top:18px">
<div class="col-md-12">
@if (isset($amount) && $client && $account->showTokenCheckbox($storageGateway/* will contain gateway id */))
<input id="token_billing" type="checkbox" name="token_billing" {{ $account->selectTokenCheckbox() ? 'CHECKED' : '' }} value="1" style="margin-left:0px; vertical-align:top">
<label for="token_billing" class="checkbox" style="display: inline;">{{ trans('texts.token_billing') }}</label>
<span class="help-block" style="font-size:15px">
@if ($storageGateway == GATEWAY_STRIPE)
{!! trans('texts.token_billing_secure', ['link' => link_to('https://stripe.com/', 'Stripe.com', ['target' => '_blank'])]) !!}
@elseif ($storageGateway == GATEWAY_BRAINTREE)
{!! trans('texts.token_billing_secure', ['link' => link_to('https://www.braintreepayments.com/', 'Braintree', ['target' => '_blank'])]) !!}
@endif
</span>
@endif
</div>
</div>
</div>
<div class="row" style="padding-top:18px">
<div class="col-md-12">
@if (isset($amount) && $client && $account->showTokenCheckbox($storageGateway/* will contain gateway id */))
<input id="token_billing" type="checkbox" name="token_billing" {{ $account->selectTokenCheckbox() ? 'CHECKED' : '' }} value="1" style="margin-left:0px; vertical-align:top">
<label for="token_billing" class="checkbox" style="display: inline;">{{ trans('texts.token_billing') }}</label>
<span class="help-block" style="font-size:15px">
@if ($storageGateway == GATEWAY_STRIPE)
{!! trans('texts.token_billing_secure', ['link' => link_to('https://stripe.com/', 'Stripe.com', ['target' => '_blank'])]) !!}
@elseif ($storageGateway == GATEWAY_BRAINTREE)
{!! trans('texts.token_billing_secure', ['link' => link_to('https://www.braintreepayments.com/', 'Braintree', ['target' => '_blank'])]) !!}
@endif
</span>
@endif
</div>
<div class="col-lg-4" style="padding-top: 12px; padding-left: 0px;">
<div class='card-wrapper'></div>
</div>
</div>
<div class="col-lg-4" style="padding-top: 12px; padding-left: 0px;">
<div class='card-wrapper'></div>
</div>
@endif
</div>
@endif

View File

@ -0,0 +1,83 @@
@extends('payments.credit_card')
@section('head')
@parent
<style type="text/css">
.paymill-form {
margin-left: 16px;
}
</style>
<script type="text/javascript">
var PAYMILL_PUBLIC_KEY = '{{ $accountGateway->getPublishableKey() }}';
</script>
<script type="text/javascript" src = "https://bridge.paymill.com/"></script>
<script type="text/javascript">
$(function() {
var options = {
lang: '{{ App::getLocale() }}',
resize: false,
};
var callback = function(error){
if (error){
console.log(error.apierror, error.message);
} else {
}
};
paymill.embedFrame('paymillCardFields', options, callback);
$('.payment-form').unbind('submit').submit(function(event) {
if ($('#sourceToken').val()) {
// do nothing
} else {
event.preventDefault();
var data = {
amount_int: {{ $invitation->invoice->getRequestedAmount() * 100 }},
currency: '{{ $invitation->invoice->getCurrencyCode() }}',
email: '{{ $contact->email }}',
};
var callback = function(error, result) {
if (error) {
if (error.apierror == 'field_invalid_card_number') {
var message = "{{ trans('texts.invalid_card_number') }}";
} else {
var message = error.apierror;
if (error.message) {
message += ': ' + error.message;
}
}
$('.payment-form').find('button').prop('disabled', false);
NINJA.formIsSubmitted = false;
$('#js-error-message').html(message).fadeIn();
} else {
$('#sourceToken').val(result.token);
$('.payment-form').submit();
}
}
if (NINJA.formIsSubmitted) {
return false;
}
NINJA.formIsSubmitted = true;
// Disable the submit button to prevent repeated clicks
$('.payment-form').find('button').prop('disabled', true);
$('#js-error-message').hide();
paymill.createTokenViaFrame(data, callback);
}
});
})
</script>
@stop

View File

@ -5,7 +5,7 @@
<script type="text/javascript" src="https://js.stripe.com/v2/"></script>
<script type="text/javascript">
Stripe.setPublishableKey('{{ $accountGateway->getPublishableStripeKey() }}');
Stripe.setPublishableKey('{{ $accountGateway->getPublishableKey() }}');
$(function() {
var countries = {!! Cache::get('countries')->pluck('iso_3166_2','id') !!};
$('.payment-form').submit(function(event) {

View File

@ -3,10 +3,10 @@
@section('head')
@parent
@if ($accountGateway->getPublishableStripeKey())
@if ($accountGateway->getPublishableKey())
<script type="text/javascript" src="https://js.stripe.com/v2/"></script>
<script type="text/javascript">
Stripe.setPublishableKey('{{ $accountGateway->getPublishableStripeKey() }}');
Stripe.setPublishableKey('{{ $accountGateway->getPublishableKey() }}');
$(function() {
$('.payment-form').unbind('submit').submit(function(event) {
event.preventDefault();

View File

@ -8,7 +8,7 @@
$(function() {
$('.payment-form').submit(function(event) {
// https://stripe.com/docs/sources/sepa-debit
var stripe = Stripe('{{ $accountGateway->getPublishableStripeKey() }}');
var stripe = Stripe('{{ $accountGateway->getPublishableKey() }}');
stripe.createSource({
type: 'sepa_debit',
sepa_debit: {