├── .gitignore ├── src ├── test │ ├── resources │ │ ├── application.properties │ │ └── logback-test.xml │ └── java │ │ └── com │ │ └── github │ │ └── woki │ │ └── payments │ │ └── adyen │ │ ├── simulator │ │ ├── APS.java │ │ └── web │ │ │ └── controller │ │ │ └── PaymentController.java │ │ ├── action │ │ └── ActionUtilEncryptionTest.java │ │ ├── ClientConfigTest.java │ │ └── ClientPaymentsTest.java └── main │ └── java │ └── com │ └── github │ └── woki │ └── payments │ └── adyen │ ├── model │ ├── GenderType.java │ ├── ContractType.java │ ├── Name.java │ ├── ShopperInteraction.java │ ├── ResultCode.java │ ├── ThreeDSecureData.java │ ├── Recurring.java │ ├── BankAccount.java │ ├── TokenDetails.java │ ├── Installments.java │ ├── FraudResult.java │ ├── ForexQuote.java │ ├── RecurringDisableResponse.java │ ├── Amount.java │ ├── ModificationResponse.java │ ├── BrowserInfo.java │ ├── ELV.java │ ├── FraudCheckResult.java │ ├── RecurringListDetailsResponse.java │ ├── Address.java │ ├── Error.java │ ├── RecurringListDetailsRequest.java │ ├── NameBuilder.java │ ├── RecurringDisableRequest.java │ ├── ModificationRequest.java │ ├── PaymentResponse.java │ ├── Card.java │ ├── ThreeDSecureDataBuilder.java │ ├── AddressBuilder.java │ ├── RecurringDetail.java │ ├── CardBuilder.java │ ├── ForexQuoteBuilder.java │ ├── ModificationRequestBuilder.java │ ├── BankAccountBuilder.java │ ├── PaymentRequest.java │ └── PaymentRequestBuilder.java │ ├── error │ ├── APSAccessException.java │ ├── NestedExceptionUtils.java │ └── NestedRuntimeException.java │ ├── action │ ├── Options.java │ ├── CancelOrRefund.java │ ├── Cancel.java │ ├── Refund.java │ ├── Capture.java │ ├── Authorise.java │ ├── RecurringDisable.java │ ├── RecurringListDetails.java │ ├── CSEUtil.java │ └── Endpoint.java │ ├── support │ ├── APUtil.java │ ├── APService.java │ └── ToStringStyle.java │ ├── IClient.java │ ├── Client.java │ └── ClientConfig.java ├── README.md ├── pom.xml └── LICENSE.md /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.settings/ 3 | /.idea/ 4 | /.classpath 5 | /.project 6 | /*.iml 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/test/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.main.banner-mode=off 2 | logging.level.org.springframework=error 3 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/GenderType.java: -------------------------------------------------------------------------------- 1 | package com.github.woki.payments.adyen.model; 2 | 3 | public enum GenderType { 4 | M, 5 | F, 6 | U 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/ContractType.java: -------------------------------------------------------------------------------- 1 | package com.github.woki.payments.adyen.model; 2 | 3 | public enum ContractType { 4 | ONECLICK, 5 | RECURRING, 6 | PAYOUT 7 | } 8 | -------------------------------------------------------------------------------- /src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/Name.java: -------------------------------------------------------------------------------- 1 | package com.github.woki.payments.adyen.model; 2 | 3 | import com.github.woki.payments.adyen.support.ToStringStyle; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | import org.apache.commons.lang3.builder.ToStringBuilder; 7 | 8 | import java.io.Serializable; 9 | 10 | @SuppressWarnings("serial") 11 | @Getter 12 | @Setter 13 | public class Name implements Serializable { 14 | private String firstName; 15 | private String lastName; 16 | private GenderType gender = GenderType.U; 17 | private String infix; 18 | 19 | @Override 20 | public String toString() { 21 | return new ToStringBuilder(this, ToStringStyle.DEFAULT_STYLE) 22 | .append("firstName", firstName) 23 | .append("lastName", lastName) 24 | .append("gender", gender) 25 | .append("infix", infix).toString(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/ShopperInteraction.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.model; 18 | 19 | /** 20 | * @author Willian Oki <willian.oki@gmail.com> 21 | */ 22 | public enum ShopperInteraction { 23 | Ecommerce, 24 | ContAuth, 25 | POS, 26 | Moto 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/ResultCode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.model; 18 | 19 | /** 20 | * @author Willian Oki <willian.oki@gmail.com> 21 | */ 22 | public enum ResultCode { 23 | Authorised, 24 | Refused, 25 | Error, 26 | Received, 27 | RedirectShopper 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/error/APSAccessException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.error; 18 | 19 | /** 20 | * @author Willian Oki <willian.oki@gmail.com> 21 | */ 22 | @SuppressWarnings("serial") 23 | public final class APSAccessException extends NestedRuntimeException { 24 | public APSAccessException(final String msg, final Throwable cause) { 25 | super(msg, cause); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/test/java/com/github/woki/payments/adyen/simulator/APS.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.simulator; 18 | 19 | import org.springframework.boot.SpringApplication; 20 | import org.springframework.boot.autoconfigure.SpringBootApplication; 21 | 22 | /** 23 | * @author Willian Oki <willian.oki@gmail.com> 24 | */ 25 | @SpringBootApplication 26 | public class APS { 27 | public static void main(String[] args) { 28 | SpringApplication.run(APS.class, args); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/ThreeDSecureData.java: -------------------------------------------------------------------------------- 1 | package com.github.woki.payments.adyen.model; 2 | 3 | import com.github.woki.payments.adyen.support.ToStringStyle; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | import org.apache.commons.lang3.builder.ToStringBuilder; 7 | 8 | import java.io.Serializable; 9 | 10 | @SuppressWarnings("serial") 11 | @Getter 12 | @Setter 13 | public class ThreeDSecureData implements Serializable { 14 | private String authenticationResponse; 15 | private String cavv; 16 | private String cavvAlgorithm; 17 | private String directoryResponse; 18 | private String eci; 19 | private String xid; 20 | 21 | @Override 22 | public String toString() { 23 | return new ToStringBuilder(this, ToStringStyle.DEFAULT_STYLE) 24 | .append("authenticationResponse", authenticationResponse) 25 | .append("cavv", cavv) 26 | .append("cavvAlgorithm", cavvAlgorithm) 27 | .append("directoryResponse", directoryResponse) 28 | .append("eci", eci) 29 | .append("xid", xid).toString(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/Recurring.java: -------------------------------------------------------------------------------- 1 | package com.github.woki.payments.adyen.model; 2 | 3 | import com.github.woki.payments.adyen.support.ToStringStyle; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | import org.apache.commons.lang3.builder.ToStringBuilder; 7 | 8 | import java.io.Serializable; 9 | 10 | @SuppressWarnings("serial") 11 | @Getter 12 | @Setter 13 | public class Recurring implements Serializable { 14 | private ContractType contract; 15 | private String recurringDetailName; 16 | private String tokenService; 17 | 18 | public Recurring() { 19 | } 20 | 21 | public Recurring(ContractType contract, String recurringDetailName, String tokenService) { 22 | this.contract = contract; 23 | this.recurringDetailName = recurringDetailName; 24 | this.tokenService = tokenService; 25 | } 26 | 27 | @Override 28 | public String toString() { 29 | return new ToStringBuilder(this, ToStringStyle.DEFAULT_STYLE) 30 | .append("contract", contract) 31 | .append("recurringDetailName", recurringDetailName) 32 | .append("tokenService", tokenService).toString(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/BankAccount.java: -------------------------------------------------------------------------------- 1 | package com.github.woki.payments.adyen.model; 2 | 3 | import com.github.woki.payments.adyen.support.ToStringStyle; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | import org.apache.commons.lang3.builder.ToStringBuilder; 7 | 8 | import java.io.Serializable; 9 | 10 | @SuppressWarnings("serial") 11 | @Getter 12 | @Setter 13 | public class BankAccount implements Serializable { 14 | private String bankAccountNumber; 15 | private String bankCity; 16 | private String bankLocationId; 17 | private String bankName; 18 | private String bic; 19 | private String countryCode = "US"; 20 | private String iban; 21 | private String ownerName; 22 | private String taxId; 23 | 24 | @Override 25 | public String toString() { 26 | return new ToStringBuilder(this, ToStringStyle.DEFAULT_STYLE) 27 | .append("bankAccountNumber", bankAccountNumber) 28 | .append("bankCity", bankCity) 29 | .append("bankLocationId", bankLocationId) 30 | .append("bankName", bankName) 31 | .append("bic", bic) 32 | .append("countryCode", countryCode) 33 | .append("iban", iban) 34 | .append("ownerName", ownerName) 35 | .append("taxId", taxId).toString(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/action/Options.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.action; 18 | 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | 22 | /** 23 | * @author Willian Oki <willian.oki@gmail.com> 24 | */ 25 | public final class Options { 26 | private Map options = new HashMap<>(); 27 | 28 | public Options addOption(final String name, final String value) { 29 | options.put(name, value); 30 | return this; 31 | } 32 | 33 | public Options addOption(final String name) { 34 | return addOption(name, ""); 35 | } 36 | 37 | public boolean has(final String name) { 38 | return options.containsKey(name); 39 | } 40 | 41 | public String getValue(final String name) { 42 | return options.get(name); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/TokenDetails.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.model; 18 | 19 | import lombok.Getter; 20 | import lombok.Setter; 21 | import org.apache.commons.lang3.builder.ToStringBuilder; 22 | import org.apache.commons.lang3.builder.ToStringStyle; 23 | 24 | import java.io.Serializable; 25 | import java.util.Map; 26 | 27 | /** 28 | * @author Willian Oki <willian.oki@gmail.com> 29 | */ 30 | @SuppressWarnings("serial") 31 | @Getter 32 | @Setter 33 | public class TokenDetails implements Serializable { 34 | private String tokenDataType; 35 | private Map tokenData; 36 | 37 | @Override 38 | public String toString() { 39 | return new ToStringBuilder(this, ToStringStyle.DEFAULT_STYLE) 40 | .append("tokenDataType", tokenDataType) 41 | .append("tokenData", tokenData).toString(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/Installments.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.model; 18 | 19 | import com.github.woki.payments.adyen.support.ToStringStyle; 20 | import lombok.Getter; 21 | import lombok.Setter; 22 | import org.apache.commons.lang3.builder.ToStringBuilder; 23 | 24 | import java.io.Serializable; 25 | 26 | /** 27 | * @author Willian Oki <willian.oki@gmail.com> 28 | */ 29 | @SuppressWarnings("serial") 30 | @Getter 31 | @Setter 32 | public class Installments implements Serializable { 33 | private Integer value; 34 | 35 | public Installments() { 36 | } 37 | 38 | public Installments(Integer value) { 39 | this.value = value; 40 | } 41 | 42 | @Override 43 | public String toString() { 44 | return new ToStringBuilder(this, ToStringStyle.DEFAULT_STYLE) 45 | .append("value", value).toString(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/FraudResult.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.model; 18 | 19 | import com.github.woki.payments.adyen.support.ToStringStyle; 20 | import lombok.Getter; 21 | import lombok.Setter; 22 | import org.apache.commons.lang3.builder.ToStringBuilder; 23 | 24 | import java.io.Serializable; 25 | import java.util.List; 26 | import java.util.Map; 27 | 28 | /** 29 | * @author Willian Oki <willian.oki@gmail.com> 30 | */ 31 | @SuppressWarnings("serial") 32 | @Getter 33 | @Setter 34 | public class FraudResult implements Serializable { 35 | private String accountScore; 36 | private List> results; 37 | 38 | @Override 39 | public String toString() { 40 | return new ToStringBuilder(this, ToStringStyle.DEFAULT_STYLE) 41 | .append("accountScore", accountScore) 42 | .append("results", results).toString(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/ForexQuote.java: -------------------------------------------------------------------------------- 1 | package com.github.woki.payments.adyen.model; 2 | 3 | import com.github.woki.payments.adyen.support.ToStringStyle; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | import org.apache.commons.lang3.builder.ToStringBuilder; 7 | 8 | import java.io.Serializable; 9 | import java.util.Date; 10 | 11 | @SuppressWarnings("serial") 12 | @Getter 13 | @Setter 14 | public class ForexQuote implements Serializable { 15 | private Integer basePoints; 16 | private Date validTill; 17 | private String account; 18 | private String accountType; 19 | private Amount baseAmount; 20 | private Amount buy; 21 | private Amount interbank; 22 | private String reference; 23 | private Amount sell; 24 | private String signature; 25 | private String source; 26 | private String type; 27 | 28 | @Override 29 | public String toString() { 30 | return new ToStringBuilder(this, ToStringStyle.DEFAULT_STYLE) 31 | .append("basePoints", basePoints) 32 | .append("validTill", validTill) 33 | .append("account", account) 34 | .append("accountType", accountType) 35 | .append("baseAmount", baseAmount) 36 | .append("buy", buy) 37 | .append("interbank", interbank) 38 | .append("reference", reference) 39 | .append("sell", sell) 40 | .append("signature", signature) 41 | .append("source", source) 42 | .append("type", type).toString(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/RecurringDisableResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.model; 18 | 19 | import lombok.Getter; 20 | import lombok.Setter; 21 | import org.apache.commons.lang3.builder.ToStringBuilder; 22 | import org.apache.commons.lang3.builder.ToStringStyle; 23 | 24 | import java.io.Serializable; 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | 28 | /** 29 | * @author Willian Oki <willian.oki@gmail.com> 30 | */ 31 | @SuppressWarnings("serial") 32 | @Getter 33 | @Setter 34 | public class RecurringDisableResponse extends Error implements Serializable { 35 | private List details = new ArrayList<>(); 36 | private String response; 37 | 38 | @Override 39 | public String toString() { 40 | return new ToStringBuilder(this, ToStringStyle.DEFAULT_STYLE) 41 | .append("details", details) 42 | .append("response", response).toString(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/Amount.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.model; 18 | 19 | import com.github.woki.payments.adyen.support.ToStringStyle; 20 | import lombok.Getter; 21 | import lombok.Setter; 22 | import org.apache.commons.lang3.builder.ToStringBuilder; 23 | 24 | import java.io.Serializable; 25 | import java.util.Currency; 26 | 27 | /** 28 | * @author Willian Oki <willian.oki@gmail.com> 29 | */ 30 | @SuppressWarnings("serial") 31 | @Getter 32 | @Setter 33 | public class Amount implements Serializable { 34 | private Currency currency; 35 | private Long value = 0L; 36 | 37 | public Amount() { 38 | } 39 | 40 | public Amount(Currency currency, long value) { 41 | this.currency = currency; 42 | this.value = value; 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | return new ToStringBuilder(this, ToStringStyle.DEFAULT_STYLE) 48 | .append("currency", currency) 49 | .append("value", value).toString(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/ModificationResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.model; 18 | 19 | import com.github.woki.payments.adyen.support.ToStringStyle; 20 | import lombok.Getter; 21 | import lombok.Setter; 22 | import org.apache.commons.lang3.builder.ToStringBuilder; 23 | 24 | import java.io.Serializable; 25 | import java.util.HashMap; 26 | import java.util.Map; 27 | 28 | /** 29 | * @author Willian Oki <willian.oki@gmail.com> 30 | */ 31 | @SuppressWarnings("serial") 32 | @Getter 33 | @Setter 34 | public class ModificationResponse extends Error implements Serializable { 35 | private Map additionalData = new HashMap<>(); 36 | private String pspReference; 37 | private String response; 38 | 39 | @Override 40 | public String toString() { 41 | return new ToStringBuilder(this, ToStringStyle.DEFAULT_STYLE) 42 | .append("additionalData", additionalData) 43 | .append("pspReference", pspReference) 44 | .append("response", response).toString(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/BrowserInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.model; 18 | 19 | import com.github.woki.payments.adyen.support.ToStringStyle; 20 | import lombok.Getter; 21 | import lombok.Setter; 22 | import org.apache.commons.lang3.builder.ToStringBuilder; 23 | 24 | import java.io.Serializable; 25 | 26 | /** 27 | * @author Willian Oki <willian.oki@gmail.com> 28 | */ 29 | @SuppressWarnings("serial") 30 | @Getter 31 | @Setter 32 | public class BrowserInfo implements Serializable { 33 | private String userAgent; 34 | private String acceptHeader; 35 | 36 | public BrowserInfo() { 37 | } 38 | 39 | public BrowserInfo(String userAgent, String acceptHeader) { 40 | this.userAgent = userAgent; 41 | this.acceptHeader = acceptHeader; 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return new ToStringBuilder(this, ToStringStyle.DEFAULT_STYLE) 47 | .append("userAgent", userAgent) 48 | .append("acceptHeader", acceptHeader).toString(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/support/APUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.support; 18 | 19 | import java.util.Date; 20 | import java.util.UUID; 21 | 22 | /** 23 | * @author Willian Oki <willian.oki@gmail.com> 24 | */ 25 | public final class APUtil { 26 | private APUtil() { 27 | // utility 28 | } 29 | 30 | public static final String TEST_ENDPOINT = "https://pal-test.adyen.com"; 31 | public static final String LIVE_ENDPOINT = "https://pal-live.adyen.com"; 32 | 33 | public enum ReferenceType { 34 | DATE, TIMESTAMP, UUID 35 | } 36 | 37 | public static String reference(final ReferenceType type) { 38 | String retval = null; 39 | switch (type) { 40 | case TIMESTAMP: 41 | retval = String.valueOf(System.currentTimeMillis()); 42 | break; 43 | case UUID: 44 | retval = UUID.randomUUID().toString(); 45 | break; 46 | case DATE: 47 | retval = new Date().toString(); 48 | break; 49 | } 50 | return retval; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/ELV.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.model; 18 | 19 | import lombok.Getter; 20 | import lombok.Setter; 21 | import org.apache.commons.lang3.builder.ToStringBuilder; 22 | import org.apache.commons.lang3.builder.ToStringStyle; 23 | 24 | import java.io.Serializable; 25 | 26 | /** 27 | * @author Willian Oki <willian.oki@gmail.com> 28 | */ 29 | @SuppressWarnings("serial") 30 | @Getter 31 | @Setter 32 | public class ELV implements Serializable { 33 | private String bankAccountNumber; 34 | private String bankLocationId; 35 | private String bankName; 36 | private String bankLocation; 37 | private String accountHolderName; 38 | 39 | @Override 40 | public String toString() { 41 | return new ToStringBuilder(this, ToStringStyle.DEFAULT_STYLE) 42 | .append("bankAccountNumber", bankAccountNumber) 43 | .append("bankLocationId", bankLocationId) 44 | .append("bankName", bankName) 45 | .append("bankLocation", bankLocation) 46 | .append("accountHolderName", accountHolderName).toString(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/FraudCheckResult.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.model; 18 | 19 | import com.github.woki.payments.adyen.support.ToStringStyle; 20 | import lombok.Getter; 21 | import lombok.Setter; 22 | import org.apache.commons.lang3.builder.ToStringBuilder; 23 | 24 | import java.io.Serializable; 25 | 26 | /** 27 | * @author Willian Oki <willian.oki@gmail.com> 28 | */ 29 | @SuppressWarnings("serial") 30 | @Getter 31 | @Setter 32 | public class FraudCheckResult implements Serializable { 33 | private Long accountScore; 34 | private Long checkId; 35 | private String name; 36 | 37 | public FraudCheckResult() { 38 | } 39 | 40 | public FraudCheckResult(String name, Long checkId, Long accountScore) { 41 | this.accountScore = accountScore; 42 | this.checkId = checkId; 43 | this.name = name; 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | return new ToStringBuilder(this, ToStringStyle.DEFAULT_STYLE) 49 | .append("accountScore", accountScore) 50 | .append("checkId", checkId) 51 | .append("name", name).toString(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/RecurringListDetailsResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.model; 18 | 19 | import lombok.Getter; 20 | import lombok.Setter; 21 | import org.apache.commons.lang3.builder.ToStringBuilder; 22 | import org.apache.commons.lang3.builder.ToStringStyle; 23 | 24 | import java.io.Serializable; 25 | import java.util.ArrayList; 26 | import java.util.Date; 27 | import java.util.List; 28 | 29 | /** 30 | * @author Willian Oki <willian.oki@gmail.com> 31 | */ 32 | @SuppressWarnings("serial") 33 | @Getter 34 | @Setter 35 | public class RecurringListDetailsResponse extends Error implements Serializable { 36 | private Date creationDate; 37 | private String lastKnownShopperEmail; 38 | private String shopperReference; 39 | private List details = new ArrayList<>(); 40 | 41 | @Override 42 | public String toString() { 43 | return new ToStringBuilder(this, ToStringStyle.DEFAULT_STYLE) 44 | .append("creationDate", creationDate) 45 | .append("lastKnownShopperEmail", lastKnownShopperEmail) 46 | .append("shopperReference", shopperReference) 47 | .append("details", details).toString(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/Address.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.model; 18 | 19 | import com.github.woki.payments.adyen.support.ToStringStyle; 20 | import com.neovisionaries.i18n.CountryCode; 21 | import lombok.Getter; 22 | import lombok.Setter; 23 | import org.apache.commons.lang3.builder.ToStringBuilder; 24 | 25 | import java.io.Serializable; 26 | 27 | /** 28 | * @author Willian Oki <willian.oki@gmail.com> 29 | */ 30 | @SuppressWarnings("serial") 31 | @Getter 32 | @Setter 33 | public class Address implements Serializable { 34 | private String street; 35 | private String houseNumberOrName; 36 | private String postalCode; 37 | private String city; 38 | private String stateOrProvince; 39 | private CountryCode country; 40 | 41 | @Override 42 | public String toString() { 43 | return new ToStringBuilder(this, ToStringStyle.DEFAULT_STYLE) 44 | .append("street", street) 45 | .append("houseNumberOrName", houseNumberOrName) 46 | .append("postalCode", postalCode) 47 | .append("city", city) 48 | .append("stateOrProvince", stateOrProvince) 49 | .append("country", country).toString(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/Error.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.model; 18 | 19 | import lombok.Getter; 20 | import lombok.Setter; 21 | 22 | import java.io.Serializable; 23 | 24 | /** 25 | * @author Willian Oki <willian.oki@gmail.com> 26 | */ 27 | @SuppressWarnings("serial") 28 | @Getter 29 | @Setter 30 | public class Error implements Serializable { 31 | private Integer status; 32 | private Integer errorCode; 33 | private String message; 34 | private String errorType; 35 | 36 | public Error() { 37 | } 38 | 39 | public Error(Integer status, Integer errorCode, String message, String errorType) { 40 | this.status = status; 41 | this.errorCode = errorCode; 42 | this.message = message; 43 | this.errorType = errorType; 44 | } 45 | 46 | public boolean isOk() { return status == 200; } 47 | 48 | public boolean isBadRequest() { return status == 400; } 49 | 50 | public boolean isUnprocessableEntity() { return status == 422; } 51 | 52 | public boolean isUnauthorized() { return status == 401; } 53 | 54 | public boolean isForbidden() { return status == 403; } 55 | 56 | public boolean isNotFound() { return status == 404; } 57 | 58 | public boolean isInternalServerError() { return status == 500; } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/RecurringListDetailsRequest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.model; 18 | 19 | import lombok.Getter; 20 | import lombok.Setter; 21 | import org.apache.commons.lang3.builder.ToStringBuilder; 22 | import org.apache.commons.lang3.builder.ToStringStyle; 23 | 24 | import java.io.Serializable; 25 | 26 | /** 27 | * @author Willian Oki <willian.oki@gmail.com> 28 | */ 29 | @SuppressWarnings("serial") 30 | @Getter 31 | @Setter 32 | public class RecurringListDetailsRequest implements Serializable { 33 | private String merchantAccount; 34 | private ContractType contract; 35 | private String shopperReference; 36 | 37 | public RecurringListDetailsRequest() { 38 | } 39 | 40 | public RecurringListDetailsRequest(String merchantAccount, ContractType contract, String shopperReference) { 41 | this.merchantAccount = merchantAccount; 42 | this.contract = contract; 43 | this.shopperReference = shopperReference; 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | return new ToStringBuilder(this, ToStringStyle.DEFAULT_STYLE) 49 | .append("merchantAccount", merchantAccount) 50 | .append("contract", contract) 51 | .append("shopperReference", shopperReference).toString(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/error/NestedExceptionUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.error; 18 | 19 | /** 20 | * Helper class for implementing exception classes which are capable of holding nested exceptions. Necessary because we can't share a base class among different exception types. 21 | *

22 | * Note: 'direct inspiration' from Spring/Core. Used here to avoid dependency on spring-core. 23 | *

24 | * 25 | * @author Willian Oki <willian.oki@gmail.com> 26 | */ 27 | public final class NestedExceptionUtils { 28 | private NestedExceptionUtils() { 29 | // utility 30 | } 31 | /** 32 | * Build a message for the given base message and root cause. 33 | * 34 | * @param message the base message 35 | * @param cause the root cause 36 | * 37 | * @return the full exception message 38 | */ 39 | public static String buildMessage(final String message, final Throwable cause) { 40 | if (cause != null) { 41 | StringBuilder sb = new StringBuilder(); 42 | if (message != null) { 43 | sb.append(message).append("; "); 44 | } 45 | sb.append("nested exception is ").append(cause); 46 | return sb.toString(); 47 | } else { 48 | return message; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/action/CancelOrRefund.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.action; 18 | 19 | import com.github.woki.payments.adyen.ClientConfig; 20 | import com.github.woki.payments.adyen.error.APSAccessException; 21 | import com.github.woki.payments.adyen.model.ModificationRequest; 22 | import com.github.woki.payments.adyen.model.ModificationResponse; 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | /** 27 | * @author Willian Oki <willian.oki@gmail.com> 28 | */ 29 | public class CancelOrRefund { 30 | private static final Logger LOG = LoggerFactory.getLogger(CancelOrRefund.class); 31 | 32 | public static ModificationResponse execute(final ClientConfig config, final ModificationRequest request) { 33 | if (LOG.isDebugEnabled()) { 34 | LOG.debug("config: {}, request: {}", config, request); 35 | } 36 | ModificationResponse retval; 37 | try { 38 | retval = Endpoint.invoke(config, request, ModificationResponse.class, new Options().addOption("action", "cancelOrRefund")); 39 | } catch (Exception e) { 40 | LOG.error("cancelOrRefund", e); 41 | throw new APSAccessException("cancelOrRefund", e); 42 | } 43 | if (LOG.isDebugEnabled()) { 44 | LOG.debug("retval: {}", retval); 45 | } 46 | return retval; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/action/Cancel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.action; 18 | 19 | import com.github.woki.payments.adyen.ClientConfig; 20 | import com.github.woki.payments.adyen.error.APSAccessException; 21 | import com.github.woki.payments.adyen.model.ModificationRequest; 22 | import com.github.woki.payments.adyen.model.ModificationResponse; 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | /** 27 | * @author Willian Oki <willian.oki@gmail.com> 28 | */ 29 | public final class Cancel { 30 | private Cancel() { 31 | // utility 32 | } 33 | 34 | private static final Logger LOG = LoggerFactory.getLogger(Cancel.class); 35 | 36 | public static ModificationResponse execute(final ClientConfig config, final ModificationRequest request) { 37 | if (LOG.isDebugEnabled()) { 38 | LOG.debug("config: {}, request: {}", config, request); 39 | } 40 | ModificationResponse retval; 41 | try { 42 | retval = Endpoint.invoke(config, request, ModificationResponse.class, new Options().addOption("action", "cancel")); 43 | } catch (Exception e) { 44 | LOG.error("cancel", e); 45 | throw new APSAccessException("cancel", e); 46 | } 47 | if (LOG.isDebugEnabled()) { 48 | LOG.debug("retval: {}", retval); 49 | } 50 | return retval; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/action/Refund.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.action; 18 | 19 | import com.github.woki.payments.adyen.ClientConfig; 20 | import com.github.woki.payments.adyen.error.APSAccessException; 21 | import com.github.woki.payments.adyen.model.ModificationRequest; 22 | import com.github.woki.payments.adyen.model.ModificationResponse; 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | /** 27 | * @author Willian Oki <willian.oki@gmail.com> 28 | */ 29 | public final class Refund { 30 | private Refund() { 31 | // utility 32 | } 33 | 34 | private static final Logger LOG = LoggerFactory.getLogger(Refund.class); 35 | 36 | public static ModificationResponse execute(final ClientConfig config, final ModificationRequest request) { 37 | if (LOG.isDebugEnabled()) { 38 | LOG.debug("config: {}, request: {}", config, request); 39 | } 40 | ModificationResponse retval; 41 | try { 42 | retval = Endpoint.invoke(config, request, ModificationResponse.class, new Options().addOption("action", "refund")); 43 | } catch (Exception e) { 44 | LOG.error("refund", e); 45 | throw new APSAccessException("refund", e); 46 | } 47 | if (LOG.isDebugEnabled()) { 48 | LOG.debug("retval: {}", retval); 49 | } 50 | return retval; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/action/Capture.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.action; 18 | 19 | import com.github.woki.payments.adyen.ClientConfig; 20 | import com.github.woki.payments.adyen.error.APSAccessException; 21 | import com.github.woki.payments.adyen.model.ModificationRequest; 22 | import com.github.woki.payments.adyen.model.ModificationResponse; 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | /** 27 | * @author Willian Oki <willian.oki@gmail.com> 28 | */ 29 | public final class Capture { 30 | private Capture() { 31 | // utility 32 | } 33 | 34 | private static final Logger LOG = LoggerFactory.getLogger(Capture.class); 35 | 36 | public static ModificationResponse execute(final ClientConfig config, final ModificationRequest request) { 37 | if (LOG.isDebugEnabled()) { 38 | LOG.debug("config: {}, request: {}", config, request); 39 | } 40 | ModificationResponse retval; 41 | try { 42 | retval = Endpoint.invoke(config, request, ModificationResponse.class, new Options().addOption("action", "capture")); 43 | } catch (Exception e) { 44 | LOG.error("capture", e); 45 | throw new APSAccessException("capture", e); 46 | } 47 | if (LOG.isDebugEnabled()) { 48 | LOG.debug("retval: {}", retval); 49 | } 50 | return retval; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/action/Authorise.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.action; 18 | 19 | import com.github.woki.payments.adyen.ClientConfig; 20 | import com.github.woki.payments.adyen.error.APSAccessException; 21 | import com.github.woki.payments.adyen.model.PaymentRequest; 22 | import com.github.woki.payments.adyen.model.PaymentResponse; 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | /** 27 | * @author Willian Oki <willian.oki@gmail.com> 28 | */ 29 | public final class Authorise { 30 | private Authorise() { 31 | // utility 32 | } 33 | 34 | private static final Logger LOG = LoggerFactory.getLogger(Authorise.class); 35 | 36 | public static PaymentResponse execute(final ClientConfig config, final PaymentRequest request, boolean threeds) { 37 | if (LOG.isDebugEnabled()) { 38 | LOG.debug("config: {}, request: {}, 3-ds: {}", config, request, threeds); 39 | } 40 | PaymentResponse retval; 41 | try { 42 | retval = Endpoint.invoke(config, request, PaymentResponse.class, threeds ? new Options().addOption("threeds") : null); 43 | } catch (Exception e) { 44 | LOG.error("authorisation", e); 45 | throw new APSAccessException("authorization", e); 46 | } 47 | if (LOG.isDebugEnabled()) { 48 | LOG.debug("retval: {}", retval); 49 | } 50 | return retval; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/action/RecurringDisable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.action; 18 | 19 | import com.github.woki.payments.adyen.ClientConfig; 20 | import com.github.woki.payments.adyen.error.APSAccessException; 21 | import com.github.woki.payments.adyen.model.RecurringDisableRequest; 22 | import com.github.woki.payments.adyen.model.RecurringDisableResponse; 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | /** 27 | * @author Willian Oki <willian.oki@gmail.com> 28 | */ 29 | public final class RecurringDisable { 30 | private RecurringDisable() { 31 | // utility 32 | } 33 | 34 | private static final Logger LOG = LoggerFactory.getLogger(RecurringDisable.class); 35 | 36 | public static RecurringDisableResponse execute(final ClientConfig config, final RecurringDisableRequest request) { 37 | if (LOG.isDebugEnabled()) { 38 | LOG.debug("config: {}, request: {}", config, request); 39 | } 40 | RecurringDisableResponse retval; 41 | try { 42 | retval = Endpoint.invoke(config, request, RecurringDisableResponse.class); 43 | } catch (Exception e) { 44 | LOG.error("recurring disable", e); 45 | throw new APSAccessException("recurring disable", e); 46 | } 47 | if (LOG.isDebugEnabled()) { 48 | LOG.debug("retval: {}", retval); 49 | } 50 | return retval; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/action/RecurringListDetails.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.action; 18 | 19 | import com.github.woki.payments.adyen.ClientConfig; 20 | import com.github.woki.payments.adyen.error.APSAccessException; 21 | import com.github.woki.payments.adyen.model.RecurringListDetailsRequest; 22 | import com.github.woki.payments.adyen.model.RecurringListDetailsResponse; 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | /** 27 | * @author Willian Oki <willian.oki@gmail.com> 28 | */ 29 | public final class RecurringListDetails { 30 | private RecurringListDetails() { 31 | // utility 32 | } 33 | 34 | private static final Logger LOG = LoggerFactory.getLogger(RecurringListDetails.class); 35 | 36 | public static RecurringListDetailsResponse execute(final ClientConfig config, final RecurringListDetailsRequest request) { 37 | if (LOG.isDebugEnabled()) { 38 | LOG.debug("config: {}, request: {}", config, request); 39 | } 40 | RecurringListDetailsResponse retval; 41 | try { 42 | retval = Endpoint.invoke(config, request, RecurringListDetailsResponse.class); 43 | } catch (Exception e) { 44 | LOG.error("recurring list details", e); 45 | throw new APSAccessException("recurring list details", e); 46 | } 47 | if (LOG.isDebugEnabled()) { 48 | LOG.debug("retval: {}", retval); 49 | } 50 | return retval; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/NameBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.model; 18 | 19 | /** 20 | * @author Willian Oki <willian.oki@gmail.com> 21 | */ 22 | public final class NameBuilder { 23 | private NameBuilder() { 24 | // utility 25 | } 26 | 27 | public static ILastName first(String first) { 28 | return new Builder(first); 29 | } 30 | 31 | public interface ILastName { 32 | IBuilder last(String last); 33 | } 34 | 35 | public interface IBuilder { 36 | IBuilder gender(GenderType gender); 37 | IBuilder infix(String infix); 38 | Name build(); 39 | } 40 | 41 | private static final class Builder implements IBuilder, ILastName { 42 | private Name name; 43 | 44 | Builder(String first) { 45 | name = new Name(); 46 | name.setFirstName(first); 47 | } 48 | 49 | @Override 50 | public Name build() { 51 | return name; 52 | } 53 | 54 | @Override 55 | public IBuilder last(String last) { 56 | name.setLastName(last); 57 | return this; 58 | } 59 | 60 | @Override 61 | public IBuilder gender(GenderType gender) { 62 | name.setGender(gender); 63 | return this; 64 | } 65 | 66 | @Override 67 | public IBuilder infix(String infix) { 68 | name.setInfix(infix); 69 | return this; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/RecurringDisableRequest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | package com.github.woki.payments.adyen.model; 19 | 20 | import com.github.woki.payments.adyen.support.ToStringStyle; 21 | import lombok.Getter; 22 | import lombok.Setter; 23 | import org.apache.commons.lang3.builder.ToStringBuilder; 24 | 25 | import java.io.Serializable; 26 | 27 | /** 28 | * @author Willian Oki <willian.oki@gmail.com> 29 | */ 30 | @SuppressWarnings("serial") 31 | @Getter 32 | @Setter 33 | public class RecurringDisableRequest implements Serializable { 34 | private String contract; 35 | private String merchantAccount; 36 | private String recurringDetailReference; 37 | private String shopperReference; 38 | 39 | public RecurringDisableRequest() { 40 | } 41 | 42 | public RecurringDisableRequest(String contract, String merchantAccount, String recurringDetailReference, String shopperReference) { 43 | this.contract = contract; 44 | this.merchantAccount = merchantAccount; 45 | this.recurringDetailReference = recurringDetailReference; 46 | this.shopperReference = shopperReference; 47 | } 48 | 49 | @Override 50 | public String toString() { 51 | return new ToStringBuilder(this, ToStringStyle.DEFAULT_STYLE) 52 | .append("contract", contract) 53 | .append("merchantAccount", merchantAccount) 54 | .append("recurringDetailReference", recurringDetailReference) 55 | .append("shopperReference", shopperReference).toString(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/ModificationRequest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.model; 18 | 19 | import com.github.woki.payments.adyen.support.ToStringStyle; 20 | import lombok.Getter; 21 | import lombok.Setter; 22 | import org.apache.commons.lang3.builder.ToStringBuilder; 23 | 24 | import java.io.Serializable; 25 | import java.util.HashMap; 26 | import java.util.Map; 27 | 28 | /** 29 | * @author Willian Oki <willian.oki@gmail.com> 30 | */ 31 | @SuppressWarnings("serial") 32 | @Getter 33 | @Setter 34 | public class ModificationRequest implements Serializable { 35 | private Map additionalData = new HashMap<>(); 36 | private String authorisationCode; 37 | private String merchantAccount; 38 | private Amount modificationAmount; 39 | private String originalReference; 40 | private String reference; 41 | private String tenderReference; 42 | private String uniqueTerminalId; 43 | 44 | @Override 45 | public String toString() { 46 | return new ToStringBuilder(this, ToStringStyle.DEFAULT_STYLE) 47 | .append("additionalData", additionalData) 48 | .append("authorisationCode", authorisationCode) 49 | .append("merchantAccount", merchantAccount) 50 | .append("modificationAmount", modificationAmount) 51 | .append("originalReference", originalReference) 52 | .append("reference", reference) 53 | .append("tenderReference", tenderReference) 54 | .append("uniqueTerminalId", uniqueTerminalId).toString(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/PaymentResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.model; 18 | 19 | import com.github.woki.payments.adyen.support.ToStringStyle; 20 | import lombok.Getter; 21 | import lombok.Setter; 22 | import org.apache.commons.lang3.builder.ToStringBuilder; 23 | 24 | import java.io.Serializable; 25 | import java.util.HashMap; 26 | import java.util.Map; 27 | 28 | /** 29 | * @author Willian Oki <willian.oki@gmail.com> 30 | */ 31 | @SuppressWarnings("serial") 32 | @Getter 33 | @Setter 34 | public class PaymentResponse extends Error implements Serializable { 35 | private Map additionalData = new HashMap<>(); 36 | private FraudResult fraudResult; 37 | private String pspReference; 38 | private String refusalReason; 39 | private ResultCode resultCode; 40 | private String authCode; 41 | private Amount dccAmount; 42 | private String dccSignature; 43 | private String issuerUrl; 44 | private String md; 45 | private String paRequest; 46 | 47 | @Override 48 | public String toString() { 49 | return new ToStringBuilder(this, ToStringStyle.DEFAULT_STYLE) 50 | .append("additionalData", additionalData) 51 | .append("authCode", authCode) 52 | .append("dccAmount", dccAmount) 53 | .append("dccSignature", dccSignature) 54 | .append("fraudResult", fraudResult) 55 | .append("issuerUrl", issuerUrl) 56 | .append("md", md) 57 | .append("paRequest", paRequest) 58 | .append("pspReference", pspReference) 59 | .append("refusalReason", refusalReason) 60 | .append("resultCode", resultCode).toString(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/error/NestedRuntimeException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.error; 18 | 19 | /** 20 | * Handy class for wrapping runtime {@code Exceptions} with a root cause. 21 | *

22 | * This class is {@code abstract} to force the programmer to extend the class. {@code getHttpErrorMessage} will include nested exception 23 | * information; {@code printStackTrace} and other like methods will delegate to the wrapped exception, if any. 24 | *

25 | *

26 | * The similarity between this class and the NestedCheckedException class is unavoidable, as Java forces these two classes to have different superclasses 27 | * (ah, the inflexibility of concrete inheritance!). 28 | *

29 | *

30 | * Note: 'direct inspiration' from Spring/Core. Used here to avoid dependency on spring-core. 31 | *

32 | * 33 | * @author Willian Oki <willian.oki@gmail.com> 34 | */ 35 | @SuppressWarnings("serial") 36 | public abstract class NestedRuntimeException extends RuntimeException { 37 | /** 38 | * Construct a {@code NestedRuntimeException} with the specified detail message. 39 | * 40 | * @param msg the detail message 41 | */ 42 | public NestedRuntimeException(final String msg) { 43 | super(msg); 44 | } 45 | 46 | /** 47 | * Construct a {@code NestedRuntimeException} with the specified detail message and nested exception. 48 | * 49 | * @param msg the detail message 50 | * @param cause the nested exception 51 | */ 52 | public NestedRuntimeException(final String msg, final Throwable cause) { 53 | super(msg, cause); 54 | } 55 | 56 | /** 57 | * Return the detail message, including the message from the nested exception if there is one. 58 | */ 59 | @Override 60 | public String getMessage() { 61 | return NestedExceptionUtils.buildMessage(super.getMessage(), getCause()); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/test/java/com/github/woki/payments/adyen/action/ActionUtilEncryptionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.action; 18 | 19 | import org.apache.commons.lang3.StringUtils; 20 | import org.junit.Assert; 21 | import org.junit.Test; 22 | 23 | import java.security.InvalidKeyException; 24 | 25 | /** 26 | * @author Willian Oki <willian.oki@gmail.com> 27 | */ 28 | public class ActionUtilEncryptionTest { 29 | private static final String PUBKEY_TEXT = "10001|9B27E6AE115FB582C795C40F19BF77C2DD6875C4E410E39AFD3A861408A3D9A97057AFFC8D7C3FE3B3314ACEC2F8C3036CB6D6212005107529E253218240DC95173E45B9856C6266BBBB05797400674C028E1F86134F6BBE752C47ADD1A35BEAB972E8F2EFBCB9057C70EDE365BF9E8C9B75E58C2ED4AE34DFF2FAFB5DC1886AE1D90B13D48F6182CA0E37881D9277AED7F745D544EBDB066E129D2B74F5B294526679956E9CFA9A83624C67621796F95CB011B133A7D2DC18934D7505A31E5EABB7E05E21FFD33F9885A30BD494ED0F174FA0630BB1A60F270E30B8F8BCC18C0FA085D938AE12D7EC2D64254615F602A07D229517F46DC31CF354B7E6E12783"; 30 | private static final String PUBKEY_TEXT_ERR1 = "foo"; 31 | private static final String PUBKEY_TEXT_ERR2 = "1|2"; 32 | 33 | @Test 34 | public void testEncryption() throws Exception { 35 | String result = CSEUtil.encrypt(CSEUtil.aesCipher(), CSEUtil.rsaCipher(PUBKEY_TEXT), "4444444444444444"); 36 | System.out.println(result); 37 | Assert.assertTrue(StringUtils.isNotBlank(result)); 38 | } 39 | 40 | @Test(expected = InvalidKeyException.class) 41 | public void testEncryptionError1() throws Exception { 42 | String result = CSEUtil.encrypt(CSEUtil.aesCipher(), CSEUtil.rsaCipher(PUBKEY_TEXT_ERR1), "4444444444444444"); 43 | System.out.println(result); 44 | } 45 | 46 | @Test(expected = IllegalArgumentException.class) 47 | public void testEncryptionError2() throws Exception { 48 | String result = CSEUtil.encrypt(CSEUtil.aesCipher(), CSEUtil.rsaCipher(PUBKEY_TEXT_ERR2), "4444444444444444"); 49 | System.out.println(result); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/Card.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.model; 18 | 19 | import com.github.woki.payments.adyen.support.ToStringStyle; 20 | import lombok.Getter; 21 | import lombok.Setter; 22 | import org.apache.commons.lang3.builder.ToStringBuilder; 23 | 24 | import java.io.Serializable; 25 | import java.text.SimpleDateFormat; 26 | import java.util.Date; 27 | import java.util.TimeZone; 28 | 29 | /** 30 | * @author Willian Oki <willian.oki@gmail.com> 31 | */ 32 | @SuppressWarnings("serial") 33 | @Getter 34 | @Setter 35 | public class Card implements Serializable { 36 | private Integer expiryMonth; 37 | private Integer expiryYear; 38 | private String holderName; 39 | private String number; 40 | private String cvc; 41 | private String generationtime; 42 | private Integer issueNumber; 43 | private Integer startMonth; 44 | private Integer startYear; 45 | 46 | private static final SimpleDateFormat GENERATION_TIME_FORMAT; 47 | 48 | static { 49 | GENERATION_TIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); 50 | GENERATION_TIME_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC")); 51 | } 52 | 53 | public static final String CARD_ENCRYPTED_ADDITIONAL_DATA_KEY_NAME = "card.encrypted.json"; 54 | 55 | public void setGenerationtime(Date generationtime) { 56 | if (generationtime != null) { 57 | this.generationtime = GENERATION_TIME_FORMAT.format(generationtime); 58 | } 59 | } 60 | 61 | @Override 62 | public String toString() { 63 | return new ToStringBuilder(this, ToStringStyle.DEFAULT_STYLE) 64 | .append("expiryMonth", expiryMonth) 65 | .append("expiryYear", expiryYear) 66 | .append("holderName", holderName) 67 | .append("cardNumber", number) 68 | .append("cvc", cvc) 69 | .append("issueNumber", issueNumber) 70 | .append("startMonth", startMonth) 71 | .append("startYear", startYear) 72 | .toString(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/ThreeDSecureDataBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.model; 18 | 19 | /** 20 | * @author Willian Oki <willian.oki@gmail.com> 21 | */ 22 | public final class ThreeDSecureDataBuilder { 23 | private ThreeDSecureDataBuilder() { 24 | // builder 25 | } 26 | 27 | public static ICavv authenticationResponse(String response) { 28 | return new Builder(response); 29 | } 30 | 31 | public interface ICavv { 32 | IDirResponse cavv(String cavv, String algorithm); 33 | } 34 | 35 | public interface IDirResponse { 36 | IEci directoryResponse(String response); 37 | } 38 | 39 | public interface IEci { 40 | IXid eci(String eci); 41 | } 42 | 43 | public interface IXid { 44 | IBuilder xid(String xid); 45 | } 46 | 47 | public interface IBuilder { 48 | ThreeDSecureData build(); 49 | } 50 | 51 | private static final class Builder implements IBuilder, ICavv, IDirResponse, IEci, IXid { 52 | private ThreeDSecureData data; 53 | 54 | Builder(String authenticationResponse) { 55 | data = new ThreeDSecureData(); 56 | data.setAuthenticationResponse(authenticationResponse); 57 | } 58 | 59 | @Override 60 | public ThreeDSecureData build() { 61 | return data; 62 | } 63 | 64 | @Override 65 | public IDirResponse cavv(String cavv, String algorithm) { 66 | data.setCavv(cavv); 67 | data.setCavvAlgorithm(algorithm); 68 | return this; 69 | } 70 | 71 | @Override 72 | public IEci directoryResponse(String response) { 73 | data.setDirectoryResponse(response); 74 | return this; 75 | } 76 | 77 | @Override 78 | public IXid eci(String eci) { 79 | data.setEci(eci); 80 | return this; 81 | } 82 | 83 | @Override 84 | public IBuilder xid(String xid) { 85 | data.setXid(xid); 86 | return this; 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/support/APService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.support; 18 | 19 | import com.github.woki.payments.adyen.action.Options; 20 | import com.github.woki.payments.adyen.model.ModificationRequest; 21 | import com.github.woki.payments.adyen.model.PaymentRequest; 22 | import com.github.woki.payments.adyen.model.RecurringDisableRequest; 23 | import com.github.woki.payments.adyen.model.RecurringListDetailsRequest; 24 | 25 | /** 26 | * @author Willian Oki <willian.oki@gmail.com> 27 | */ 28 | public enum APService { 29 | AUTHORISATION("/pal/servlet/Payment/v30/authorise"), 30 | AUTHORISATION_3D("/pal/servlet/Payment/v30/authorise3d"), 31 | CAPTURE("/pal/servlet/Payment/v30/capture"), 32 | REFUND("/pal/servlet/Payment/v30/refund"), 33 | CANCEL("/pal/servlet/Payment/v30/cancel"), 34 | CANCEL_OR_REFUND("/pal/servlet/Payment/v30/cancelOrRefund"), 35 | RECURRING_DISABLE("/pal/servlet/Recurring/v30/disable"), 36 | RECURRING_LIST_DETAILS("/pal/servlet/Recurring/v30/listRecurringDetails") 37 | ; 38 | 39 | final String path; 40 | 41 | APService(String path) { 42 | this.path = path; 43 | } 44 | 45 | public String getPath() { 46 | return path; 47 | } 48 | 49 | public static APService from(final ReqType request, final Options opts) { 50 | if (request instanceof PaymentRequest) { 51 | if (opts != null && opts.has("threeds")) { 52 | return AUTHORISATION_3D; 53 | } 54 | return AUTHORISATION; 55 | } 56 | if (request instanceof ModificationRequest) { 57 | if ("capture".equals(opts.getValue("action"))) { 58 | return CAPTURE; 59 | } 60 | if ("refund".equals(opts.getValue("action"))) { 61 | return REFUND; 62 | } 63 | if ("cancel".equals(opts.getValue("action"))) { 64 | return CANCEL; 65 | } 66 | if ("cancelOrRefund".equals(opts.getValue("action"))) { 67 | return CANCEL_OR_REFUND; 68 | } 69 | } 70 | if (request instanceof RecurringDisableRequest) { 71 | return RECURRING_DISABLE; 72 | } 73 | if (request instanceof RecurringListDetailsRequest) { 74 | return RECURRING_LIST_DETAILS; 75 | } 76 | return null; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/AddressBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.model; 18 | 19 | import com.neovisionaries.i18n.CountryCode; 20 | 21 | /** 22 | * @author Willian Oki <willian.oki@gmail.com> 23 | */ 24 | public final class AddressBuilder { 25 | private AddressBuilder() { 26 | // utility 27 | } 28 | 29 | public static INumber street(String street) { 30 | return new Builder(street); 31 | } 32 | 33 | public interface INumber { 34 | IPostalCode numberOrName(String number); 35 | } 36 | 37 | public interface IPostalCode { 38 | ICity postalCode(String postalCode); 39 | } 40 | 41 | public interface ICity { 42 | IState city(String city); 43 | } 44 | 45 | public interface IState { 46 | ICountry state(String state); 47 | } 48 | 49 | public interface ICountry { 50 | IBuilder country(CountryCode country); 51 | } 52 | 53 | public interface IBuilder { 54 | Address build(); 55 | } 56 | 57 | private static final class Builder implements IBuilder, INumber, IPostalCode, ICity, IState, ICountry { 58 | private Address address; 59 | 60 | Builder(String street) { 61 | address = new Address(); 62 | address.setStreet(street); 63 | } 64 | 65 | @Override 66 | public Address build() { 67 | return address; 68 | } 69 | 70 | @Override 71 | public IBuilder country(CountryCode country) { 72 | address.setCountry(country); 73 | return this; 74 | } 75 | 76 | @Override 77 | public ICountry state(String state) { 78 | address.setStateOrProvince(state); 79 | return this; 80 | } 81 | 82 | @Override 83 | public IState city(String city) { 84 | address.setCity(city); 85 | return this; 86 | } 87 | 88 | @Override 89 | public IPostalCode numberOrName(String number) { 90 | address.setHouseNumberOrName(number); 91 | return this; 92 | } 93 | 94 | @Override 95 | public ICity postalCode(String postalCode) { 96 | address.setPostalCode(postalCode); 97 | return this; 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/RecurringDetail.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.model; 18 | 19 | import lombok.Getter; 20 | import lombok.Setter; 21 | import org.apache.commons.lang3.builder.ToStringBuilder; 22 | import org.apache.commons.lang3.builder.ToStringStyle; 23 | 24 | import java.io.Serializable; 25 | import java.util.Date; 26 | import java.util.List; 27 | import java.util.Map; 28 | 29 | /** 30 | * @author Willian Oki <willian.oki@gmail.com> 31 | */ 32 | @SuppressWarnings("serial") 33 | @Getter 34 | @Setter 35 | public class RecurringDetail implements Serializable { 36 | private String recurringDetailReference; 37 | private String name; 38 | private String variant; 39 | private String paymentMethodVariant; 40 | private Card card; 41 | private ELV elv; 42 | private BankAccount bank; 43 | private TokenDetails tokenDetails; 44 | private Name shopperName; 45 | private String socialSecurityNumber; 46 | private Address billingAddress; 47 | private String alias; 48 | private String aliasType; 49 | private String firstPspReference; 50 | private Date creationDate; 51 | private Map additionalData; 52 | private List contractTypes; 53 | private String acquirer; 54 | private String acquirerAccount; 55 | 56 | @Override 57 | public String toString() { 58 | return new ToStringBuilder(this, ToStringStyle.DEFAULT_STYLE) 59 | .append("recurringDetailReference", recurringDetailReference) 60 | .append("name", name) 61 | .append("variant", variant) 62 | .append("paymentMethodVariant", paymentMethodVariant) 63 | .append("card", card) 64 | .append("elv", elv) 65 | .append("bank", bank) 66 | .append("tokenDetails", tokenDetails) 67 | .append("shopperName", shopperName) 68 | .append("socialSecurityNumber", socialSecurityNumber) 69 | .append("billingAddress", billingAddress) 70 | .append("alias", alias) 71 | .append("aliasType", aliasType) 72 | .append("firstPspReference", firstPspReference) 73 | .append("creationDate", creationDate) 74 | .append("additionalData", additionalData) 75 | .append("contractTypes", contractTypes) 76 | .append("acquirer", acquirer) 77 | .append("acquirerAccount", acquirerAccount).toString(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/CardBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.model; 18 | 19 | /** 20 | * @author Willian Oki <willian.oki@gmail.com> 21 | */ 22 | public final class CardBuilder { 23 | private CardBuilder() { 24 | // utility 25 | } 26 | 27 | public static ICvc number(String number) { 28 | return new Builder(number); 29 | } 30 | 31 | public interface ICvc { 32 | IExpiry cvc(String cvc); 33 | } 34 | 35 | public interface IExpiry { 36 | IHolder expiry(Integer year, Integer month); 37 | } 38 | 39 | public interface IHolder { 40 | IBuilder holder(String holder); 41 | } 42 | 43 | public interface IBuilder { 44 | IBuilder issueNumber(Integer issueNumber); 45 | 46 | IBuilder startMonth(Integer startMonth); 47 | 48 | IBuilder startYear(Integer startYear); 49 | 50 | Card build(); 51 | } 52 | 53 | private static final class Builder implements IBuilder, ICvc, IExpiry, IHolder { 54 | private Card card; 55 | 56 | Builder(String number) { 57 | card = new Card(); 58 | card.setNumber(number); 59 | } 60 | 61 | @Override 62 | public IBuilder holder(String holder) { 63 | card.setHolderName(holder); 64 | return this; 65 | } 66 | 67 | @Override 68 | public IHolder expiry(Integer year, Integer month) { 69 | card.setExpiryMonth(month); 70 | card.setExpiryYear(year); 71 | return this; 72 | } 73 | 74 | @Override 75 | public IExpiry cvc(String cvc) { 76 | card.setCvc(cvc); 77 | return this; 78 | } 79 | 80 | @Override 81 | public IBuilder issueNumber(Integer issueNumber) { 82 | card.setIssueNumber(issueNumber); 83 | return this; 84 | } 85 | 86 | @Override 87 | public IBuilder startMonth(Integer startMonth) { 88 | card.setStartMonth(startMonth); 89 | return this; 90 | } 91 | 92 | @Override 93 | public IBuilder startYear(Integer startYear) { 94 | card.setStartYear(startYear); 95 | return this; 96 | } 97 | 98 | @Override 99 | public Card build() { 100 | return card; 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/support/ToStringStyle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.support; 18 | 19 | /** 20 | * @author Willian Oki <willian.oki@gmail.com> 21 | */ 22 | public class ToStringStyle extends org.apache.commons.lang3.builder.ToStringStyle { 23 | private static final int CARD_MIN_LEN = 14; 24 | private static final int CARD_BIN_LEN = 6; 25 | private static final int CARD_SUFFIX_LEN = 4; 26 | private static final int CARD_UNMASKED_LEN = CARD_BIN_LEN + CARD_SUFFIX_LEN; 27 | private static final char CARD_MASK_CHAR = '*'; 28 | 29 | public static final ToStringStyle DEFAULT_STYLE = new ToStringStyle(); 30 | 31 | public ToStringStyle() { 32 | super(); 33 | setUseShortClassName(true); 34 | setUseIdentityHashCode(false); 35 | } 36 | 37 | @Override 38 | protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) { 39 | switch (fieldName) { 40 | case "cardNumber": 41 | buffer.append(maskCardNumber((String) value)); 42 | break; 43 | case "cvc": 44 | String original = (String) value; 45 | buffer.append(extractMask(original.length(), 0, CARD_MASK_CHAR)); 46 | break; 47 | default: 48 | buffer.append(value); 49 | } 50 | } 51 | 52 | private static String maskCardNumber(final String number) { 53 | if (number == null || number.length() < CARD_MIN_LEN) { 54 | return ""; 55 | } 56 | return extractCardBin(number) + extractMask(number.length(), CARD_UNMASKED_LEN, CARD_MASK_CHAR) + extractCardSuffix(number); 57 | } 58 | 59 | private static String extractCardBin(final String number) { 60 | if (number != null && number.length() >= CARD_MIN_LEN) { 61 | return number.substring(0, CARD_BIN_LEN); 62 | } 63 | return ""; 64 | } 65 | 66 | private static String extractCardSuffix(final String number) { 67 | if (number != null && number.length() >= CARD_MIN_LEN) { 68 | return number.substring(number.length() - CARD_SUFFIX_LEN); 69 | } 70 | return ""; 71 | } 72 | 73 | private static String extractMask(int originalLength, int unmaskedLength, char mask) { 74 | int maskLength = originalLength - unmaskedLength; 75 | if (maskLength <= 0) { 76 | return ""; 77 | } 78 | 79 | char[] buff = new char[maskLength]; 80 | while (maskLength > 0) { 81 | maskLength--; 82 | buff[maskLength] = mask; 83 | } 84 | return new String(buff); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/test/java/com/github/woki/payments/adyen/simulator/web/controller/PaymentController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.simulator.web.controller; 18 | 19 | import com.github.woki.payments.adyen.model.PaymentRequest; 20 | import com.github.woki.payments.adyen.model.PaymentResponse; 21 | import org.springframework.http.HttpStatus; 22 | import org.springframework.http.ResponseEntity; 23 | import org.springframework.web.bind.annotation.RequestBody; 24 | import org.springframework.web.bind.annotation.RequestMapping; 25 | import org.springframework.web.bind.annotation.RequestMethod; 26 | import org.springframework.web.bind.annotation.RestController; 27 | 28 | /** 29 | * @author Willian Oki <willian.oki@gmail.com> 30 | */ 31 | @RestController 32 | public class PaymentController { 33 | @RequestMapping(value = {"/pal/servlet/Payment/v30/authorise", "/pal/servlet/Payment/v30/authorise3d"}, method = RequestMethod.POST) 34 | public ResponseEntity authorize(@RequestBody PaymentRequest request) { 35 | PaymentResponse res = new PaymentResponse(); 36 | if ("gimme_500".equals(request.getReference())) { 37 | res.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); 38 | return new ResponseEntity<>(res, HttpStatus.INTERNAL_SERVER_ERROR); 39 | } 40 | if ("gimme_400".equals(request.getReference())) { 41 | res.setStatus(HttpStatus.BAD_REQUEST.value()); 42 | return new ResponseEntity<>(res, HttpStatus.BAD_REQUEST); 43 | } 44 | if ("gimme_422".equals(request.getReference())) { 45 | res.setStatus(HttpStatus.UNPROCESSABLE_ENTITY.value()); 46 | return new ResponseEntity<>(res, HttpStatus.UNPROCESSABLE_ENTITY); 47 | } 48 | if ("gimme_401".equals(request.getReference())) { 49 | res.setStatus(HttpStatus.UNAUTHORIZED.value()); 50 | return new ResponseEntity<>(res, HttpStatus.UNAUTHORIZED); 51 | } 52 | if ("gimme_403".equals(request.getReference())) { 53 | res.setStatus(HttpStatus.FORBIDDEN.value()); 54 | return new ResponseEntity<>(res, HttpStatus.FORBIDDEN); 55 | } 56 | if ("gimme_404".equals(request.getReference())) { 57 | res.setStatus(HttpStatus.NOT_FOUND.value()); 58 | return new ResponseEntity<>(res, HttpStatus.NOT_FOUND); 59 | } 60 | if ("gimme_200".equals(request.getReference())) { 61 | res.setStatus(HttpStatus.OK.value()); 62 | return new ResponseEntity<>(res, HttpStatus.OK); 63 | } 64 | res.setStatus(HttpStatus.NON_AUTHORITATIVE_INFORMATION.value()); 65 | return new ResponseEntity<>(res, HttpStatus.NON_AUTHORITATIVE_INFORMATION); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/IClient.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen; 18 | 19 | import com.github.woki.payments.adyen.error.APSAccessException; 20 | import com.github.woki.payments.adyen.model.*; 21 | 22 | /** 23 | * @author Willian Oki <willian.oki@gmail.com> 24 | */ 25 | public interface IClient { 26 | /** 27 | * @return the {@link ClientConfig} 28 | */ 29 | ClientConfig getClientConfig(); 30 | /** 31 | * @param request the request 32 | * 33 | * @return the response 34 | * 35 | * @throws APSAccessException on communication error 36 | */ 37 | PaymentResponse authorise(final PaymentRequest request); 38 | /** 39 | * @param request the request 40 | * 41 | * @return the response 42 | * 43 | * @throws APSAccessException on communication error 44 | */ 45 | PaymentResponse authorise3ds(final PaymentRequest request); 46 | /** 47 | * @param request the request 48 | * 49 | * @return the response 50 | * 51 | * @throws APSAccessException on communication error 52 | */ 53 | PaymentResponse verifyBin(final PaymentRequest request); 54 | /** 55 | * @param request the request 56 | * 57 | * @return the response 58 | * 59 | * @throws APSAccessException on communication error 60 | */ 61 | ModificationResponse capture(final ModificationRequest request); 62 | /** 63 | * @param request the request 64 | * 65 | * @return the response 66 | * 67 | * @throws APSAccessException on communication error 68 | */ 69 | ModificationResponse cancel(final ModificationRequest request); 70 | /** 71 | * @param request the request 72 | * 73 | * @return the response 74 | * 75 | * @throws APSAccessException on communication error 76 | */ 77 | ModificationResponse refund(final ModificationRequest request); 78 | /** 79 | * @param request the request 80 | * 81 | * @return the response 82 | * 83 | * @throws APSAccessException on communication error 84 | */ 85 | ModificationResponse cancelOrRefund(final ModificationRequest request); 86 | /** 87 | * @param request the request 88 | * 89 | * @return the response 90 | * 91 | * @throws APSAccessException on communication error 92 | */ 93 | RecurringDisableResponse recurringDisable(final RecurringDisableRequest request); 94 | /** 95 | * @param request the request 96 | * 97 | * @return the response 98 | * 99 | * @throws APSAccessException on communication error 100 | */ 101 | RecurringListDetailsResponse recurringListDetails(final RecurringListDetailsRequest request); 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/ForexQuoteBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.model; 18 | 19 | import java.util.Date; 20 | 21 | /** 22 | * @author Willian Oki <willian.oki@gmail.com> 23 | */ 24 | public final class ForexQuoteBuilder { 25 | private ForexQuoteBuilder() { 26 | // builder 27 | } 28 | 29 | public static IValidTill base(String type, String reference, Integer basePoints) { 30 | return new Builder(type, reference, basePoints); 31 | } 32 | 33 | public interface IValidTill { 34 | IAccount validTill(Date date); 35 | } 36 | 37 | public interface IAccount { 38 | IAmounts account(String account, String accountType); 39 | } 40 | 41 | public interface IAmounts { 42 | ISource amounts(Amount base, Amount interbank, Amount buy, Amount sell); 43 | } 44 | 45 | public interface ISource { 46 | ISignature source(String source); 47 | } 48 | 49 | public interface ISignature { 50 | IBuilder signature(String signature); 51 | } 52 | 53 | public interface IBuilder { 54 | ForexQuote build(); 55 | } 56 | 57 | private static final class Builder implements IBuilder, IValidTill, IAccount, IAmounts, ISource, ISignature { 58 | private ForexQuote quote; 59 | 60 | Builder(String type, String reference, Integer basePoints) { 61 | quote = new ForexQuote(); 62 | quote.setType(type); 63 | quote.setReference(reference); 64 | quote.setBasePoints(basePoints); 65 | } 66 | 67 | @Override 68 | public IAmounts account(String account, String accountType) { 69 | quote.setAccount(account); 70 | quote.setAccountType(accountType); 71 | return this; 72 | } 73 | 74 | @Override 75 | public ISource amounts(Amount base, Amount interbank, Amount buy, Amount sell) { 76 | quote.setBaseAmount(base); 77 | quote.setInterbank(interbank); 78 | quote.setBuy(buy); 79 | quote.setSell(sell); 80 | return this; 81 | } 82 | 83 | @Override 84 | public ForexQuote build() { 85 | return quote; 86 | } 87 | 88 | @Override 89 | public IBuilder signature(String signature) { 90 | quote.setSignature(signature); 91 | return this; 92 | } 93 | 94 | @Override 95 | public ISignature source(String source) { 96 | quote.setSource(source); 97 | return this; 98 | } 99 | 100 | @Override 101 | public IAccount validTill(Date date) { 102 | quote.setValidTill(date); 103 | return this; 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/ModificationRequestBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.model; 18 | 19 | /** 20 | * @author Willian Oki <willian.oki@gmail.com> 21 | */ 22 | public final class ModificationRequestBuilder { 23 | private ModificationRequestBuilder() { 24 | // utility 25 | } 26 | 27 | public static IOriginalReference merchantAccount(String account) { 28 | return new Builder(account); 29 | } 30 | 31 | public interface IOriginalReference { 32 | IBuilder originalReference(String reference); 33 | } 34 | 35 | public interface IBuilder { 36 | IBuilder additionalData(String key, String value); 37 | IBuilder authorisationCode(String code); 38 | IBuilder reference(String reference); 39 | IBuilder modificationAmount(Amount amount); 40 | IBuilder tenderReference(String tenderReference); 41 | IBuilder uniqueTerminalId(String uniqueTerminalId); 42 | ModificationRequest build(); 43 | } 44 | 45 | private static final class Builder implements IOriginalReference, IBuilder { 46 | private ModificationRequest request; 47 | 48 | Builder(String merchantAccount) { 49 | request = new ModificationRequest(); 50 | request.setMerchantAccount(merchantAccount); 51 | } 52 | 53 | @Override 54 | public IBuilder reference(String reference) { 55 | request.setReference(reference); 56 | return this; 57 | } 58 | 59 | @Override 60 | public ModificationRequest build() { 61 | return request; 62 | } 63 | 64 | @Override 65 | public IBuilder originalReference(String reference) { 66 | request.setOriginalReference(reference); 67 | return this; 68 | } 69 | 70 | @Override 71 | public IBuilder modificationAmount(Amount amount) { 72 | request.setModificationAmount(amount); 73 | return this; 74 | } 75 | 76 | @Override 77 | public IBuilder tenderReference(String tenderReference) { 78 | request.setTenderReference(tenderReference); 79 | return this; 80 | } 81 | 82 | @Override 83 | public IBuilder uniqueTerminalId(String uniqueTerminalId) { 84 | request.setUniqueTerminalId(uniqueTerminalId); 85 | return this; 86 | } 87 | 88 | @Override 89 | public IBuilder authorisationCode(String code) { 90 | request.setAuthorisationCode(code); 91 | return this; 92 | } 93 | 94 | @Override 95 | public IBuilder additionalData(String key, String value) { 96 | request.getAdditionalData().put(key, value); 97 | return this; 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/BankAccountBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.model; 18 | 19 | /** 20 | * @author Willian Oki <willian.oki@gmail.com> 21 | */ 22 | public final class BankAccountBuilder { 23 | private BankAccountBuilder() { 24 | // utility 25 | } 26 | 27 | public static ILocationId accountNumber(String accountNumber) { 28 | return new Builder(accountNumber); 29 | } 30 | 31 | public interface ILocationId { 32 | IBankName locationId(String locationId); 33 | } 34 | 35 | public interface IBankName { 36 | IBankCity bankName(String name); 37 | } 38 | 39 | public interface IBankCity { 40 | IBic bankCity(String city); 41 | } 42 | 43 | public interface IBic { 44 | ICountryCode bic(String bic); 45 | } 46 | 47 | public interface ICountryCode { 48 | IIban countryCode(String countryCode); 49 | } 50 | 51 | public interface IIban { 52 | IOwnerName iban(String iban); 53 | } 54 | 55 | public interface IOwnerName { 56 | ITaxId owner(String owner); 57 | } 58 | 59 | public interface ITaxId { 60 | IBuilder taxId(String taxId); 61 | } 62 | 63 | public interface IBuilder { 64 | BankAccount build(); 65 | } 66 | 67 | private static final class Builder implements IBuilder, ILocationId, IBankName, IBankCity, IBic, ICountryCode, IIban, IOwnerName, ITaxId { 68 | private BankAccount account; 69 | 70 | Builder(String accountNumber) { 71 | account = new BankAccount(); 72 | account.setBankAccountNumber(accountNumber); 73 | } 74 | 75 | @Override 76 | public IBankCity bankName(String name) { 77 | account.setBankName(name); 78 | return this; 79 | } 80 | 81 | @Override 82 | public ICountryCode bic(String bic) { 83 | account.setBic(bic); 84 | return this; 85 | } 86 | 87 | @Override 88 | public BankAccount build() { 89 | return account; 90 | } 91 | 92 | @Override 93 | public IIban countryCode(String countryCode) { 94 | account.setCountryCode(countryCode); 95 | return this; 96 | } 97 | 98 | @Override 99 | public IOwnerName iban(String iban) { 100 | account.setIban(iban); 101 | return this; 102 | } 103 | 104 | @Override 105 | public IBankName locationId(String locationId) { 106 | account.setBankLocationId(locationId); 107 | return this; 108 | } 109 | 110 | @Override 111 | public ITaxId owner(String owner) { 112 | account.setOwnerName(owner); 113 | return this; 114 | } 115 | 116 | @Override 117 | public IBic bankCity(String city) { 118 | account.setBankCity(city); 119 | return this; 120 | } 121 | 122 | @Override 123 | public IBuilder taxId(String taxId) { 124 | account.setTaxId(taxId); 125 | return this; 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/test/java/com/github/woki/payments/adyen/ClientConfigTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen; 18 | 19 | import com.github.woki.payments.adyen.support.APService; 20 | import com.github.woki.payments.adyen.support.APUtil; 21 | import org.apache.http.HttpHost; 22 | import org.junit.Assert; 23 | import org.junit.Test; 24 | 25 | /** 26 | * @author Willian Oki <willian.oki@gmail.com> 27 | */ 28 | public class ClientConfigTest { 29 | private static final String PROXY_CONFIG = "prxyusr:prxypass@prxyhost:8888"; 30 | private static final String PROXY_CONFIG_2 = "62.63.4.10:8888"; 31 | 32 | @Test 33 | public void testClientConfigCreation() throws Exception { 34 | ClientConfig config = new ClientConfig(APUtil.TEST_ENDPOINT); 35 | Assert.assertTrue(config.getEndpointHost().equals(HttpHost.create(APUtil.TEST_ENDPOINT))); 36 | Assert.assertTrue(config.getEndpointPort(APService.AUTHORISATION).equals(APUtil.TEST_ENDPOINT + APService.AUTHORISATION.getPath())); 37 | Assert.assertTrue(config.getEndpointPort(APService.AUTHORISATION_3D).equals(APUtil.TEST_ENDPOINT + APService.AUTHORISATION_3D.getPath())); 38 | Assert.assertTrue(config.getEndpointPort(APService.CANCEL).equals(APUtil.TEST_ENDPOINT + APService.CANCEL.getPath())); 39 | Assert.assertTrue(config.getEndpointPort(APService.CANCEL_OR_REFUND).equals(APUtil.TEST_ENDPOINT + APService.CANCEL_OR_REFUND.getPath())); 40 | Assert.assertTrue(config.getEndpointPort(APService.CAPTURE).equals(APUtil.TEST_ENDPOINT + APService.CAPTURE.getPath())); 41 | Assert.assertTrue(config.getEndpointPort(APService.REFUND).equals(APUtil.TEST_ENDPOINT + APService.REFUND.getPath())); 42 | Assert.assertTrue(config.getConnectionTimeout() == 0); 43 | Assert.assertTrue(config.getSocketTimeout() == 0); 44 | Assert.assertFalse(config.hasProxy()); 45 | } 46 | 47 | @Test(expected = IllegalArgumentException.class) 48 | public void testClientConfigCreationFailure1() throws Exception { 49 | ClientConfig config = new ClientConfig(""); 50 | } 51 | 52 | @Test(expected = IllegalArgumentException.class) 53 | public void testClientConfigCreationFailure2() throws Exception { 54 | ClientConfig config = new ClientConfig(null); 55 | } 56 | 57 | @Test(expected = IllegalArgumentException.class) 58 | public void testClientConfigCreationFailure3() throws Exception { 59 | ClientConfig config = new ClientConfig("blah-blah"); 60 | } 61 | 62 | @Test 63 | public void testClientConfigProxyStuff() throws Exception { 64 | ClientConfig config = new ClientConfig(APUtil.TEST_ENDPOINT); 65 | config.setProxyConfig(PROXY_CONFIG); 66 | Assert.assertTrue(config.hasProxy()); 67 | Assert.assertTrue(config.isProxyAuthenticated()); 68 | Assert.assertTrue(config.getProxyUsername().equals("prxyusr")); 69 | Assert.assertTrue(config.getProxyPassword().equals("prxypass")); 70 | Assert.assertTrue(config.getProxyHost().equals(HttpHost.create("prxyhost:8888"))); 71 | } 72 | 73 | @Test 74 | public void testClientConfigProxyStuff2() throws Exception { 75 | ClientConfig config = new ClientConfig(APUtil.TEST_ENDPOINT); 76 | config.setProxyConfig(PROXY_CONFIG_2); 77 | Assert.assertTrue(config.hasProxy()); 78 | Assert.assertFalse(config.isProxyAuthenticated()); 79 | Assert.assertTrue(config.getProxyUsername() == null); 80 | Assert.assertTrue(config.getProxyPassword() == null); 81 | Assert.assertTrue(config.getProxyHost().equals(HttpHost.create(PROXY_CONFIG_2))); 82 | } 83 | 84 | @Test 85 | public void testToString() { 86 | ClientConfig config = new ClientConfig(APUtil.TEST_ENDPOINT); 87 | config.setProxyConfig(PROXY_CONFIG); 88 | System.out.println(config); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/action/CSEUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.action; 18 | 19 | import org.apache.commons.codec.binary.Base64; 20 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 21 | 22 | import javax.crypto.*; 23 | import javax.crypto.spec.IvParameterSpec; 24 | import java.math.BigInteger; 25 | import java.security.*; 26 | import java.security.spec.InvalidKeySpecException; 27 | import java.security.spec.RSAPublicKeySpec; 28 | import java.util.Locale; 29 | 30 | /** 31 | * @author Willian Oki <willian.oki@gmail.com> 32 | */ 33 | public final class CSEUtil { 34 | private static final String CSE_VERSION = "1_0_0"; 35 | private static final String CSE_SEPARATOR = "$"; 36 | private static final String CSE_PREFIX = "payments-adyen-api_"; 37 | private static final SecureRandom CSE_RANDOM = new SecureRandom(); 38 | 39 | static { 40 | Security.addProvider(new BouncyCastleProvider()); 41 | } 42 | 43 | private CSEUtil() { 44 | // utility class 45 | } 46 | 47 | private static SecretKey aesKey(int keySize) throws NoSuchAlgorithmException { 48 | KeyGenerator kgen = KeyGenerator.getInstance("AES"); 49 | kgen.init(keySize); 50 | return kgen.generateKey(); 51 | } 52 | 53 | private static synchronized byte[] iv(SecureRandom random, int ivSize) { 54 | byte[] iv = new byte[ivSize]; 55 | random.nextBytes(iv); 56 | return iv; 57 | } 58 | 59 | static String encrypt(final Cipher aesCipher, final Cipher rsaCipher, final String plainText) throws BadPaddingException, IllegalBlockSizeException, 60 | NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException { 61 | SecretKey aesKey = aesKey(256); 62 | byte[] iv = iv(CSE_RANDOM, 12); 63 | aesCipher.init(Cipher.ENCRYPT_MODE, aesKey, new IvParameterSpec(iv)); 64 | byte[] encrypted = aesCipher.doFinal(plainText.getBytes()); 65 | 66 | byte[] result = new byte[iv.length + encrypted.length]; 67 | System.arraycopy(iv, 0, result, 0, iv.length); 68 | System.arraycopy(encrypted, 0, result, iv.length, encrypted.length); 69 | 70 | byte[] encryptedAESKey; 71 | try { 72 | encryptedAESKey = rsaCipher.doFinal(aesKey.getEncoded()); 73 | } catch (ArrayIndexOutOfBoundsException e) { 74 | throw new InvalidKeyException(e.getMessage()); 75 | } 76 | return String.format("%s%s%s%s%s%s", CSE_PREFIX, CSE_VERSION, CSE_SEPARATOR, Base64.encodeBase64String(encryptedAESKey), CSE_SEPARATOR, 77 | Base64.encodeBase64String(result)); 78 | } 79 | 80 | public static Cipher aesCipher() throws NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException { 81 | return Cipher.getInstance("AES/CCM/NoPadding", "BC"); 82 | } 83 | 84 | public static Cipher rsaCipher(final String cseKeyText) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, InvalidKeySpecException, IllegalArgumentException { 85 | String[] cseKeyParts = cseKeyText.split("\\|"); 86 | if (cseKeyParts.length != 2) { 87 | throw new InvalidKeyException("Invalid CSE Key: " + cseKeyText); 88 | } 89 | KeyFactory keyFactory = KeyFactory.getInstance("RSA"); 90 | 91 | BigInteger keyComponent1, keyComponent2; 92 | try { 93 | keyComponent1 = new BigInteger(cseKeyParts[1].toLowerCase(Locale.getDefault()), 16); 94 | keyComponent2 = new BigInteger(cseKeyParts[0].toLowerCase(Locale.getDefault()), 16); 95 | } catch (NumberFormatException e) { 96 | throw new InvalidKeyException("Invalid CSE Key: " + cseKeyText); 97 | } 98 | RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(keyComponent1, keyComponent2); 99 | PublicKey pubKey = keyFactory.generatePublic(pubKeySpec); 100 | 101 | Cipher result = Cipher.getInstance("RSA/None/PKCS1Padding"); 102 | result.init(Cipher.ENCRYPT_MODE, pubKey); 103 | return result; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/Client.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen; 18 | 19 | import com.github.woki.payments.adyen.action.*; 20 | import com.github.woki.payments.adyen.model.*; 21 | 22 | import java.util.Map; 23 | 24 | /** 25 | * @author Willian Oki <willian.oki@gmail.com> 26 | */ 27 | public final class Client implements IClient { 28 | private ClientConfig config; 29 | 30 | private Client() { 31 | // disable default constructor 32 | } 33 | 34 | public interface IBuilder { 35 | IBuilder timeout(long connectionTimeout, long readTimeout); 36 | 37 | IBuilder connectionTimeout(long timeout); 38 | 39 | IBuilder readTimeout(long timeout); 40 | 41 | IBuilder extraParameters(final Map extraParameters); 42 | 43 | IBuilder proxyConfig(final String config); 44 | 45 | IBuilder encryptionKey(final String encryptionKey); 46 | 47 | IBuilder addExtraParameter(final String key, final String value); 48 | 49 | Client build(); 50 | } 51 | 52 | public static IAccount endpoint(final String endpoint) { 53 | return new Builder(endpoint); 54 | } 55 | 56 | public interface IAccount { 57 | IBuilder credentials(final String username, final String password); 58 | } 59 | 60 | private static final class Builder implements IAccount, IBuilder { 61 | private Client instance = new Client(); 62 | 63 | private Builder() { 64 | // disable default constructor 65 | } 66 | 67 | Builder(final String endpoint) { 68 | instance.config = new ClientConfig(endpoint); 69 | } 70 | 71 | @Override 72 | public IBuilder timeout(long connectionTimeout, long readTimeout) { 73 | instance.config.setConnectionTimeout((int) connectionTimeout); 74 | instance.config.setSocketTimeout((int) readTimeout); 75 | return this; 76 | } 77 | 78 | @Override 79 | public IBuilder connectionTimeout(long timeout) { 80 | instance.config.setConnectionTimeout((int) timeout); 81 | return this; 82 | } 83 | 84 | @Override 85 | public IBuilder readTimeout(long timeout) { 86 | instance.config.setSocketTimeout((int) timeout); 87 | return this; 88 | } 89 | 90 | @Override 91 | public IBuilder extraParameters(final Map extraParameters) { 92 | instance.config.setExtraParameters(extraParameters); 93 | return this; 94 | } 95 | 96 | @Override 97 | public IBuilder proxyConfig(final String config) { 98 | instance.config.setProxyConfig(config); 99 | return this; 100 | } 101 | 102 | @Override 103 | public IBuilder encryptionKey(final String encryptionKey) { 104 | instance.config.setEncryptionKey(encryptionKey); 105 | return this; 106 | } 107 | 108 | @Override 109 | public IBuilder addExtraParameter(final String key, final String value) { 110 | instance.config.addExtraParameter(key, value); 111 | return this; 112 | } 113 | 114 | @Override 115 | public Client build() { 116 | return instance; 117 | } 118 | 119 | @Override 120 | public IBuilder credentials(final String username, final String password) { 121 | instance.config.setUsername(username); 122 | instance.config.setPassword(password); 123 | return this; 124 | } 125 | } 126 | 127 | @Override 128 | public ClientConfig getClientConfig() { 129 | return config; 130 | } 131 | 132 | @Override 133 | public PaymentResponse authorise(final PaymentRequest request) { 134 | return Authorise.execute(config, request, false); 135 | } 136 | 137 | @Override 138 | public PaymentResponse authorise3ds(final PaymentRequest request) { 139 | return Authorise.execute(config, request, true); 140 | } 141 | 142 | @Override 143 | public PaymentResponse verifyBin(final PaymentRequest request) { 144 | return Authorise.execute(config, request, false); 145 | } 146 | 147 | @Override 148 | public ModificationResponse capture(final ModificationRequest request) { 149 | return Capture.execute(config, request); 150 | } 151 | 152 | @Override 153 | public ModificationResponse cancel(final ModificationRequest request) { 154 | return Cancel.execute(config, request); 155 | } 156 | 157 | @Override 158 | public ModificationResponse refund(final ModificationRequest request) { 159 | return Refund.execute(config, request); 160 | } 161 | 162 | @Override 163 | public ModificationResponse cancelOrRefund(final ModificationRequest request) { 164 | return CancelOrRefund.execute(config, request); 165 | } 166 | 167 | @Override 168 | public RecurringDisableResponse recurringDisable(final RecurringDisableRequest request) { 169 | return RecurringDisable.execute(config, request); 170 | } 171 | 172 | @Override 173 | public RecurringListDetailsResponse recurringListDetails(final RecurringListDetailsRequest request) { 174 | return RecurringListDetails.execute(config, request); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/PaymentRequest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.model; 18 | 19 | import com.github.woki.payments.adyen.support.ToStringStyle; 20 | import lombok.Getter; 21 | import lombok.Setter; 22 | import org.apache.commons.lang3.StringUtils; 23 | import org.apache.commons.lang3.builder.ToStringBuilder; 24 | 25 | import java.io.Serializable; 26 | import java.util.Date; 27 | import java.util.HashMap; 28 | import java.util.Map; 29 | 30 | /** 31 | * @author Willian Oki <willian.oki@gmail.com> 32 | */ 33 | @SuppressWarnings("serial") 34 | @Getter 35 | @Setter 36 | public class PaymentRequest implements Serializable { 37 | private Amount additionalAmount; 38 | private Map additionalData = new HashMap<>(); 39 | private Amount amount; 40 | private Address billingAddress; 41 | private BrowserInfo browserInfo; 42 | private Integer captureDelayHours; 43 | private Date dateOfBirth; 44 | private ForexQuote dccQuote; 45 | private Address deliveryAddress; 46 | private String deliveryDate; 47 | private String deviceFingerprint; 48 | private Long fraudOffset; 49 | private Installments installments; 50 | private Integer mcc; 51 | private String merchantAccount; 52 | private String merchantOrderReference; 53 | private Map metadata = new HashMap<>(); 54 | private String orderReference; 55 | private Recurring recurring; 56 | private String recurringProcessingModel; 57 | private String reference; 58 | private String selectedBrand; 59 | private String selectedRecurringDetailReference; 60 | private String sessionId; 61 | private String shopperEmail; 62 | private String shopperIP; 63 | private ShopperInteraction shopperInteraction; 64 | private String shopperLocale; 65 | private Name shopperName; 66 | private String shopperReference; 67 | private String shopperStatement; 68 | private String socialSecurityNumber; 69 | private String store; 70 | private String telephoneNumber; 71 | private String totalsGroup; 72 | private BankAccount bankAccount; 73 | private Card card; 74 | private String entityType; 75 | private ThreeDSecureData mpiData; 76 | private String nationality; 77 | private String md; 78 | private String paResponse; 79 | 80 | public void addAdditionalDataEntry(String key, String value) { 81 | if (StringUtils.isNotBlank(key)) { 82 | additionalData.put(key, value); 83 | } 84 | } 85 | 86 | // allowed values for the recurringProcessingModel field 87 | public enum ProcessingModel { 88 | SUBSCRIPTION( "Subscription"), 89 | CARDONFILE("CardOnFile"); 90 | 91 | private final String modelName; 92 | 93 | ProcessingModel(String s) { 94 | modelName = s; 95 | } 96 | 97 | public String toString() { 98 | return this.modelName; 99 | } 100 | } 101 | 102 | @Override 103 | public String toString() { 104 | return new ToStringBuilder(this, ToStringStyle.DEFAULT_STYLE) 105 | .append("additionalAmount", additionalAmount) 106 | .append("additionalData", additionalData) 107 | .append("amount", amount) 108 | .append("bankAccount", bankAccount) 109 | .append("billingAddress", billingAddress) 110 | .append("browserInfo", browserInfo) 111 | .append("captureDelayHours", captureDelayHours) 112 | .append("card", card) 113 | .append("dateOfBirth", dateOfBirth) 114 | .append("dccQuote", dccQuote) 115 | .append("deliveryAddress", deliveryAddress) 116 | .append("deliveryDate", deliveryDate) 117 | .append("deviceFingerprint", deviceFingerprint) 118 | .append("fraudOffset", fraudOffset) 119 | .append("installments", installments) 120 | .append("mcc", mcc).append("md", md) 121 | .append("merchantAccount", merchantAccount) 122 | .append("merchantOrderReference", merchantOrderReference) 123 | .append("mpiData", mpiData) 124 | .append("orderReference", orderReference) 125 | .append("paResponse", paResponse) 126 | .append("recurring", recurring) 127 | .append("recurringProcessingModel", recurringProcessingModel) 128 | .append("reference", reference) 129 | .append("selectedBrand", selectedBrand) 130 | .append("selectedRecurringDetailReference", selectedRecurringDetailReference) 131 | .append("sessionId", sessionId) 132 | .append("shopperEmail", shopperEmail) 133 | .append("shopperIP", shopperIP) 134 | .append("shopperInteraction", shopperInteraction) 135 | .append("shopperLocale", shopperLocale) 136 | .append("shopperName", shopperName) 137 | .append("shopperReference", shopperReference) 138 | .append("shopperStatement", shopperStatement) 139 | .append("socialSecurityNumber", socialSecurityNumber) 140 | .append("telephoneNumber", telephoneNumber) 141 | .append("metadata", metadata) 142 | .append("store", store) 143 | .append("totalsGroup", totalsGroup) 144 | .append("entityType", entityType) 145 | .append("nationality", nationality).toString(); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Java integration for Adyen Payment System API 2 | 3 | [Adyen](http://www.adyen.com) 4 | > is the leading technology provider powering payments for global commerce in the 21st century. 5 | > With a seamless solution for mobile, online and in-store transactions, our technology enables merchants to accept almost any 6 | > type of payment, anywhere in the world. 7 | 8 | Adyen Payment System, **APS**, lies at the heart of its payment platform and it's the service a merchant integrates with for 9 | payment processing. 10 | 11 | **adyen-api** aims to be a cohesive and opinionated way for consuming APS' JSON messaging based services. 12 | 13 | ## Acknowledgements 14 | * [Chrischy](https://github.com/Golddragon152) - Proxy configuration and CSE 15 | * [Cleverbug](https://github.com/cleverbug) - Upgrade to consume Adyen's endpoints V18 16 | 17 | ## Milestones 18 | * 2.30.0cts - instituted CTS-local version of woki project, updated for v30 and recurringProcessingModel. This branch is not intended as a source for pulling code back to woki, but rather for creating local artifacts. See details in PAYMENT-704. 19 | * 2.25.1 - Fixed URIs for v25. 20 | * 2.25.0 - Updated to cover Adyen's endpoints new version, v25. 21 | * 2.18.1 - Bug fixes; code rationalizations; tests correctness; replaced boon's JSON ObjectMapper w/ Jackson's to properly handle atypical collections 22 | in the response. 23 | * 2.18.0 - Added full support for recurring resources. 24 | * 1.3.1 - Updated to cover Adyen's endpoints new version, v18; changes were across Card, BankAccount and PaymentRequest types. 25 | * 1.3.0 - Added support for CSE. 26 | * 1.2.1 - Added support for proxy configuration. 27 | * 1.0.0 - Initial. 28 | 29 | From version 2.* on the minor version number is used to show what Adyen's API version adyen-api is compliant with. For example, 30 | 2.25.0 relates to the first release covering Adyen's V25 and so on. 31 | 32 | ## Current version and Maven dependency 33 | 34 | ```xml 35 | 36 | com.github.woki 37 | payments-adyen-api 38 | 2.25.1 39 | 40 | ``` 41 | See also this [Sample Client](http://github.com/woki/adyen-client) sample built upon **ayden-api**. 42 | It takes an authorization or modification request in YAML format and communicates with APS. 43 | 44 | ## Usage 45 | 46 | ### Client instantiation 47 | ```java 48 | Client client = Client 49 | .endpoint("https://pal-test.adyen.com") 50 | // .endpoint(APUtil.TEST_ENDPOINT) 51 | // .endpoint("https://pal-live.adyen.com") 52 | // .endpoint(APUtil.LIVE_ENDPOINT) 53 | .credentials(username, password) 54 | .build(); 55 | ``` 56 | In case you are behind a proxy just add .proxyConfig() to the composition, as follows 57 | ```java 58 | Client client = Client 59 | .endpoint("https://pal-test.adyen.com") 60 | .credentials(username, password) 61 | .proxyConfig("prxyusr:prxypass@prxysrvr:8888") 62 | .build(); 63 | ``` 64 | Notice that authentication is optional. For the example above the proxy configuration descriptor would then be like 65 | this: prxysrvr:8888. Either names or IP addresses can be used as the host name. 66 | 67 | #### CSE - Client Side Encryption 68 | For Adyen's CSE documentation and usage refer to [CSE Documentation](https://docs.adyen.com/developers/easy-encryption). Once you have generated a RSA public key 69 | just instantiate the Client like this: 70 | ```java 71 | Client client = Client 72 | .endpoint("https://pal-test.adyen.com") 73 | .credentials(username, password) 74 | .encryptionKey("10001|FBF867B24626DE756...") // key abbreviated for clarity sake 75 | .build(); 76 | ``` 77 | The Client will encrypt sensitive card information according to CSE specifications in case there's an encryption key defined. 78 | 79 | ### Authorisation 80 | ```java 81 | PaymentRequest request = PaymentRequestBuilder 82 | .merchantAccount(merchantAccount) 83 | .amount(new Amount(Currency.getInstance("EUR"), 1000L)) 84 | .card(CardBuilder.number("4111111111111111").cvc("737").expiry(2016, 6).holder("Johnny Tester Visa").build()) 85 | .reference(reference(ReferenceType.UUID)) 86 | .shopper(NameBuilder.first("Willian").last("Oki").build(), "willian.oki@gmail.com", "127.0.0.1", 87 | "Test/DAPI/Authorisation/Willian Oki", ShopperInteraction.Ecommerce) 88 | .build(); 89 | PaymentResponse response = client.authorise(request); 90 | 91 | // you can check if response is valid using response.isOk() 92 | ``` 93 | 94 | ### Capture 95 | ```java 96 | ModificationRequest captureRequest = ModificationRequestBuilder 97 | .merchantAccount(merchantAccount) 98 | .modificationAmount(new Amount(Currency.getInstance("EUR"), 1000L)) 99 | .originalReference(paymentResponse.getPspReference()) 100 | .reference(reference(ReferenceType.UUID)) 101 | .build(); 102 | ModificationResponse captureResponse = client.capture(captureRequest); 103 | 104 | // you can check if response is valid using response.isOk() 105 | ``` 106 | 107 | ### Cancel/Refund 108 | ```java 109 | ModificationRequest cancelRequest = ModificationRequestBuilder 110 | .merchantAccount(merchantAccount) 111 | .originalReference(paymentResponse.getPspReference()) 112 | .reference(reference(ReferenceType.UUID)) 113 | .build(); 114 | ModificationResponse cancelResponse = client.cancel(cancelRequest); 115 | 116 | // you can check if response is valid using response.isOk() 117 | ``` 118 | ```java 119 | ModificationRequest refundRequest = ModificationRequestBuilder 120 | .merchantAccount(merchantAccount) 121 | .modificationAmount(new Amount(Currency.getInstance("EUR"), 1000L)) 122 | .originalReference(paymentResponse.getPspReference()) 123 | .reference(reference(ReferenceType.UUID)) 124 | .build(); 125 | ModificationResponse refundResponse = client.refund(refundRequest); 126 | 127 | // you can check if response is valid using response.isOk() 128 | ``` 129 | ```java 130 | ModificationRequest cancelOrRefundRequest = ModificationRequestBuilder 131 | .merchantAccount(merchantAccount) 132 | .originalReference(paymentResponse.getPspReference()) 133 | .reference(reference(ReferenceType.UUID)) 134 | .build(); 135 | ModificationResponse cancelOrRefundResponse = client.cancelOrRefund(cancelOrRefundRequest); 136 | 137 | // you can check if response is valid using response.isOk() 138 | ``` 139 | 140 | ### Recurring 141 | 142 | #### List Details 143 | ```java 144 | // possible ContractType: ONECLICK, RECURRING, PAYOUT 145 | RecurringListDetailsRequest req = new RecurringListDetailsRequest("yourMerchantAccount", ContractType.ONECLICK, 146 | "yourShopperReference"); 147 | RecurringListDetailsResponse res = client.recurringListDetails(req); 148 | 149 | // you can check if response is valid using response.isOk() 150 | 151 | // ... 152 | ``` 153 | 154 | #### Disable 155 | ```java 156 | RecurringDisableRequest req = new RecurringDisableRequest("yourContract", "yourMerchantAccount", 157 | "yourRecurringDetailReference", "yourShopperReference"); 158 | RecurringDisableResponse res = client.recurringDisable(req); 159 | 160 | // you can check if response is valid using response.isOk() 161 | 162 | // ... 163 | ``` 164 | -------------------------------------------------------------------------------- /src/test/java/com/github/woki/payments/adyen/ClientPaymentsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen; 18 | 19 | import com.github.woki.payments.adyen.error.APSAccessException; 20 | import com.github.woki.payments.adyen.model.*; 21 | import com.github.woki.payments.adyen.simulator.APS; 22 | import com.github.woki.payments.adyen.support.APUtil; 23 | import com.github.woki.payments.adyen.support.APUtil.ReferenceType; 24 | import org.junit.Before; 25 | import org.junit.Test; 26 | import org.junit.runner.RunWith; 27 | import org.springframework.boot.context.embedded.LocalServerPort; 28 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; 29 | import org.springframework.boot.test.context.SpringBootTest; 30 | import org.springframework.test.context.junit4.SpringRunner; 31 | 32 | import java.util.Currency; 33 | 34 | import static com.github.woki.payments.adyen.support.APUtil.reference; 35 | import static org.hamcrest.MatcherAssert.assertThat; 36 | import static org.hamcrest.Matchers.is; 37 | import static org.hamcrest.Matchers.notNullValue; 38 | 39 | /** 40 | * @author Willian Oki <willian.oki@gmail.com> 41 | */ 42 | @RunWith(SpringRunner.class) 43 | @SpringBootTest(classes = {APS.class}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 44 | @AutoConfigureMockMvc 45 | public class ClientPaymentsTest { 46 | private static final String PUBKEY_TEXT_ERR1 = "foo"; 47 | private static final String PUBKEY_TEXT_ERR2 = "1|2"; 48 | 49 | @LocalServerPort 50 | private int port; 51 | 52 | private Client client; 53 | private PaymentRequest req; 54 | 55 | @Before 56 | public void setUp() { 57 | client = Client 58 | .endpoint("http://localhost:" + port) 59 | .credentials("test_user", "test_password") 60 | .build(); 61 | req = PaymentRequestBuilder 62 | .merchantAccount("test_merchant_account") 63 | .amount(new Amount(Currency.getInstance("USD"), 10)) 64 | .build(); 65 | } 66 | 67 | @Test(expected = APSAccessException.class) 68 | public void testClientCSEError() { 69 | Client cli = Client 70 | .endpoint(APUtil.TEST_ENDPOINT) 71 | .credentials("merchant", "password") 72 | .encryptionKey(PUBKEY_TEXT_ERR1) 73 | .build(); 74 | cli.authorise(PaymentRequestBuilder 75 | .merchantAccount("mrchntacct") 76 | .amount(new Amount(Currency.getInstance("EUR"), 1000L)) 77 | .card(CardBuilder 78 | .number("4111111111111111") 79 | .cvc("737") 80 | .expiry(2016, 6) 81 | .holder("Johnny Tester Visa") 82 | .build()) 83 | .reference(reference(ReferenceType.UUID)) 84 | .shopper(NameBuilder 85 | .first("Willian") 86 | .last("Oki") 87 | .build(), "willian.oki@gmail.com", "127.0.0.1", "Test/DAPI/Authorisation/Willian Oki", ShopperInteraction.Ecommerce) 88 | .build()); 89 | } 90 | 91 | @Test(expected = APSAccessException.class) 92 | public void testClientCSEError2() { 93 | Client cli = Client.endpoint(APUtil.TEST_ENDPOINT).credentials("merchant", "password").encryptionKey(PUBKEY_TEXT_ERR2).build(); 94 | cli.authorise(PaymentRequestBuilder.merchantAccount("mrchntacct").amount(new Amount(Currency.getInstance("EUR"), 1000L)) 95 | .card(CardBuilder.number("4111111111111111").cvc("737").expiry(2016, 6).holder("Johnny Tester Visa").build()) 96 | .reference(reference(ReferenceType.UUID)).shopper(NameBuilder.first("Willian").last("Oki").build(), 97 | "willian.oki@gmail.com", "127.0.0.1", "Test/DAPI/Authorisation/Willian Oki", ShopperInteraction.Ecommerce).build()); 98 | } 99 | 100 | @Test 101 | public void testHttp200handling() throws Exception { 102 | req.setReference("gimme_200"); 103 | PaymentResponse res = client.authorise(req); 104 | assertThat(res, notNullValue()); 105 | assertThat(res.isOk(), is(true)); 106 | res = client.authorise3ds(req); 107 | assertThat(res, notNullValue()); 108 | assertThat(res.isOk(), is(true)); 109 | } 110 | 111 | @Test 112 | public void testHttp500handling() throws Exception { 113 | req.setReference("gimme_500"); 114 | PaymentResponse res = client.authorise(req); 115 | assertThat(res, notNullValue()); 116 | assertThat(res.isInternalServerError(), is(true)); 117 | assertThat(res.getMessage(), is("Unexpected error")); 118 | res = client.authorise3ds(req); 119 | assertThat(res, notNullValue()); 120 | assertThat(res.isInternalServerError(), is(true)); 121 | assertThat(res.getMessage(), is("Unexpected error")); 122 | } 123 | 124 | @Test 125 | public void testHttp400handling() throws Exception { 126 | req.setReference("gimme_400"); 127 | PaymentResponse res = client.authorise(req); 128 | assertThat(res, notNullValue()); 129 | assertThat(res.isBadRequest(), is(true)); 130 | assertThat(res.getMessage(), is("Problem reading or understanding request")); 131 | res = client.authorise3ds(req); 132 | assertThat(res, notNullValue()); 133 | assertThat(res.isBadRequest(), is(true)); 134 | assertThat(res.getMessage(), is("Problem reading or understanding request")); 135 | } 136 | 137 | @Test 138 | public void testHttp422handling() throws Exception { 139 | req.setReference("gimme_422"); 140 | PaymentResponse res = client.authorise(req); 141 | assertThat(res, notNullValue()); 142 | assertThat(res.isUnprocessableEntity(), is(true)); 143 | assertThat(res.getMessage(), is("Request validation error")); 144 | res = client.authorise3ds(req); 145 | assertThat(res, notNullValue()); 146 | assertThat(res.isUnprocessableEntity(), is(true)); 147 | assertThat(res.getMessage(), is("Request validation error")); 148 | } 149 | 150 | @Test 151 | public void testHttp401handling() throws Exception { 152 | req.setReference("gimme_401"); 153 | PaymentResponse res = client.authorise(req); 154 | assertThat(res, notNullValue()); 155 | assertThat(res.isUnauthorized(), is(true)); 156 | assertThat(res.getMessage(), is("Authentication required")); 157 | res = client.authorise3ds(req); 158 | assertThat(res, notNullValue()); 159 | assertThat(res.isUnauthorized(), is(true)); 160 | assertThat(res.getMessage(), is("Authentication required")); 161 | } 162 | 163 | @Test 164 | public void testHttp403handling() throws Exception { 165 | req.setReference("gimme_403"); 166 | PaymentResponse res = client.authorise(req); 167 | assertThat(res, notNullValue()); 168 | assertThat(res.isForbidden(), is(true)); 169 | assertThat(res.getMessage(), is("Insufficient permission to process request")); 170 | res = client.authorise3ds(req); 171 | assertThat(res, notNullValue()); 172 | assertThat(res.isForbidden(), is(true)); 173 | assertThat(res.getMessage(), is("Insufficient permission to process request")); 174 | } 175 | 176 | @Test 177 | public void testHttp404handling() throws Exception { 178 | req.setReference("gimme_404"); 179 | PaymentResponse res = client.authorise(req); 180 | assertThat(res, notNullValue()); 181 | assertThat(res.isNotFound(), is(true)); 182 | assertThat(res.getMessage(), is("Service not found")); 183 | res = client.authorise3ds(req); 184 | assertThat(res, notNullValue()); 185 | assertThat(res.isNotFound(), is(true)); 186 | assertThat(res.getMessage(), is("Service not found")); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/ClientConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen; 18 | 19 | import com.github.woki.payments.adyen.action.CSEUtil; 20 | import com.github.woki.payments.adyen.support.APService; 21 | import com.github.woki.payments.adyen.support.APUtil; 22 | import com.github.woki.payments.adyen.support.ToStringStyle; 23 | import org.apache.commons.lang3.StringUtils; 24 | import org.apache.commons.lang3.builder.ToStringBuilder; 25 | import org.apache.http.HttpHost; 26 | import org.apache.http.client.utils.URIUtils; 27 | import org.slf4j.Logger; 28 | import org.slf4j.LoggerFactory; 29 | 30 | import javax.crypto.Cipher; 31 | import java.net.URI; 32 | import java.util.HashMap; 33 | import java.util.Map; 34 | import java.util.regex.Matcher; 35 | import java.util.regex.Pattern; 36 | 37 | /** 38 | * @author Willian Oki <willian.oki@gmail.com> 39 | */ 40 | public class ClientConfig { 41 | private HttpHost endpointHost; 42 | private int connectionTimeout; 43 | private int socketTimeout; 44 | private String proxyConfig; 45 | private HttpHost proxyHost; 46 | private String endpoint; 47 | private String username; 48 | private String password; 49 | private String proxyUsername, proxyPassword; 50 | private Map extraParameters = new HashMap<>(); 51 | private String encryptionKey; 52 | private Cipher aesCipher; 53 | private Cipher rsaCipher; 54 | 55 | private static final Logger LOG = LoggerFactory.getLogger(ClientConfig.class); 56 | private static final Pattern PROXY_CONFIG_PATTERN = Pattern.compile("(.*):(.*)@([a-zA-Z0-9\\.:]+):(\\d+)|([a-zA-Z0-9\\.:]+):(\\d+)"); 57 | 58 | /** 59 | * Constructor 60 | * 61 | * @param endpoint the endpoint; {@link APUtil#TEST_ENDPOINT} / {@link APUtil#LIVE_ENDPOINT} 62 | * 63 | * @throws IllegalArgumentException on invalid URI 64 | */ 65 | public ClientConfig(final String endpoint) { 66 | if (StringUtils.isBlank(endpoint)) { 67 | throw new IllegalArgumentException("Invalid endpoint: " + endpoint); 68 | } 69 | endpointHost = URIUtils.extractHost(URI.create(endpoint)); 70 | if (endpointHost == null) { 71 | throw new IllegalArgumentException("Invalid endpoint: " + endpoint); 72 | } 73 | this.endpoint = endpoint; 74 | } 75 | 76 | /** 77 | * connectionTimeout (millisecs) see http.connection.timeout (httpclient). 0 (default) means no timeout (blocking). 78 | * 79 | * @return the connection timeout 80 | */ 81 | public int getConnectionTimeout() { 82 | return connectionTimeout; 83 | } 84 | 85 | void setConnectionTimeout(int connectionTimeout) { 86 | this.connectionTimeout = connectionTimeout; 87 | } 88 | 89 | /** 90 | * readTimeout (millisecs) see http.socket.timeout (httpclient). 0 (default) means no timeout (blocking). 91 | * 92 | * @return the read timeout 93 | */ 94 | public int getSocketTimeout() { 95 | return socketTimeout; 96 | } 97 | 98 | void setSocketTimeout(int socketTimeout) { 99 | this.socketTimeout = socketTimeout; 100 | } 101 | 102 | public String getEndpointPort(final APService service) { 103 | return endpoint + service.getPath(); 104 | } 105 | 106 | /** 107 | * @return the username 108 | */ 109 | public String getUsername() { 110 | return username; 111 | } 112 | 113 | /** 114 | * @param username the username to set 115 | */ 116 | void setUsername(final String username) { 117 | this.username = username; 118 | } 119 | 120 | /** 121 | * @return the password 122 | */ 123 | public String getPassword() { 124 | return password; 125 | } 126 | 127 | /** 128 | * @param password the password to set 129 | */ 130 | void setPassword(final String password) { 131 | this.password = password; 132 | } 133 | 134 | /** 135 | * @return extra parameters map 136 | */ 137 | public Map getExtraParameters() { 138 | return extraParameters; 139 | } 140 | 141 | /** 142 | * @param extraParameters the extra parameters map to set 143 | */ 144 | void setExtraParameters(final Map extraParameters) { 145 | this.extraParameters = extraParameters; 146 | } 147 | 148 | /** 149 | * @param key the extra parameter key 150 | * @param value the extra parameter value 151 | */ 152 | void addExtraParameter(final String key, final String value) { 153 | extraParameters.put(key, value); 154 | } 155 | 156 | /** 157 | * Proxy's Hostname/IP, port and credentials formatted as follow: 158 | *

159 | * [user:password@]host:port
160 | * E.g.: prxyusr:prxypass@proxy:8888, prxyusr:prxypass@127.0.0.1:8888, proxy:8888, ... 161 | *

162 | * 163 | * @param proxyConfig the specification 164 | */ 165 | void setProxyConfig(final String proxyConfig) { 166 | this.proxyConfig = proxyConfig; 167 | } 168 | 169 | public HttpHost getEndpointHost() { 170 | return endpointHost; 171 | } 172 | 173 | public boolean hasProxy() { 174 | return getProxyHost() != null; 175 | } 176 | 177 | public HttpHost getProxyHost() { 178 | if (proxyHost == null && proxyConfig != null) { 179 | Matcher matcher = PROXY_CONFIG_PATTERN.matcher(proxyConfig); 180 | if (matcher.matches() && matcher.groupCount() == 6) { 181 | if (matcher.group(1) == null) { 182 | proxyHost = HttpHost.create(matcher.group(5) + ":" + matcher.group(6)); 183 | } else { 184 | proxyUsername = matcher.group(1); 185 | proxyPassword = matcher.group(2); 186 | proxyHost = HttpHost.create(matcher.group(3) + ":" + matcher.group(4)); 187 | } 188 | } 189 | } 190 | return proxyHost; 191 | } 192 | 193 | public boolean isProxyAuthenticated() { 194 | return proxyUsername != null; 195 | } 196 | 197 | public String getProxyUsername() { 198 | return hasProxy() ? proxyUsername : null; 199 | } 200 | 201 | public String getProxyPassword() { 202 | return hasProxy() ? proxyPassword : null; 203 | } 204 | 205 | public String getEncryptionKey() { 206 | return encryptionKey; 207 | } 208 | 209 | void setEncryptionKey(final String encryptionKey) { 210 | this.encryptionKey = encryptionKey; 211 | } 212 | 213 | public Cipher getAesCipher() { 214 | if (StringUtils.isNotBlank(encryptionKey) && aesCipher == null) { 215 | try { 216 | aesCipher = CSEUtil.aesCipher(); 217 | } catch (Exception e) { 218 | LOG.warn("Could not instantiate an AES Cipher", e); 219 | } 220 | } 221 | return aesCipher; 222 | } 223 | 224 | public Cipher getRsaCipher() { 225 | if (StringUtils.isNotBlank(encryptionKey) && rsaCipher == null) { 226 | try { 227 | rsaCipher = CSEUtil.rsaCipher(encryptionKey); 228 | } catch (Exception e) { 229 | LOG.warn("Could not instantiate an RSA Cipher. encryptionKey: {}", encryptionKey, e); 230 | } 231 | } 232 | return rsaCipher; 233 | } 234 | 235 | @Override 236 | public String toString() { 237 | return new ToStringBuilder(this, ToStringStyle.DEFAULT_STYLE) 238 | .append("endpointHost", endpointHost) 239 | .append("connectionTimeout", connectionTimeout) 240 | .append("socketTimeout", socketTimeout) 241 | .append("proxyConfig", proxyConfig) 242 | .append("proxyHost", proxyHost) 243 | .append("endpoint", endpoint) 244 | .append("username", username) 245 | .append("password", password) 246 | .append("proxyUsername", proxyUsername) 247 | .append("proxyPassword", proxyPassword) 248 | .append("extraParameters", extraParameters) 249 | .append("encryptionKey", encryptionKey).toString(); 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.github.woki 5 | payments-adyen-api 6 | 2.25.2-SNAPSHOT 7 | jar 8 | 9 | ${project.groupId}:${project.artifactId} 10 | adyen-api is a cohesive and opinionated way for consuming APS' JSON messaging based services 11 | 12 | https://github.com/woki/adyen-api 13 | 14 | 15 | The Apache License, Version 2.0 16 | http://www.apache.org/licenses/LICENSE-2.0.txt 17 | 18 | 19 | 20 | 21 | Willian Oki 22 | willian.oki@gmail.com 23 | 24 | 25 | 26 | scm:git:git@github.com:woki/adyen-api.git 27 | scm:git:git@github.com:woki/adyen-api.git 28 | git@github.com:woki/adyen-api.git 29 | 30 | 31 | 32 | UTF-8 33 | UTF-8 34 | 1.7 35 | 3.6 36 | 1.3.2 37 | 4.5.3 38 | 1.22 39 | 2.8.9 40 | 1.16.16 41 | 1.57 42 | 3.6.1 43 | 3.0.1 44 | 2.10.4 45 | 1.6 46 | 1.6.8 47 | 48 | 49 | 50 | 51 | ossrh 52 | https://oss.sonatype.org/content/repositories/snapshots 53 | 54 | 55 | 56 | 57 | 58 | 59 | org.apache.maven.plugins 60 | maven-compiler-plugin 61 | ${maven-compiler-plugin.version} 62 | 63 | ${java.version} 64 | ${java.version} 65 | 66 | 67 | 68 | org.sonatype.plugins 69 | nexus-staging-maven-plugin 70 | ${nexus-staging-maven-plugin.version} 71 | true 72 | 73 | ossrh 74 | https://oss.sonatype.org/ 75 | true 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | release 84 | 85 | 86 | 87 | org.apache.maven.plugins 88 | maven-source-plugin 89 | ${maven-source-plugin.version} 90 | 91 | 92 | attach-sources 93 | 94 | jar 95 | 96 | 97 | 98 | 99 | 100 | org.apache.maven.plugins 101 | maven-javadoc-plugin 102 | ${maven-javadoc-plugin.version} 103 | 104 | 105 | attach-javadocs 106 | 107 | jar 108 | 109 | 110 | 111 | 112 | 113 | org.apache.maven.plugins 114 | maven-gpg-plugin 115 | ${maven-gpg-plugin.version} 116 | 117 | 118 | sign-artifacts 119 | verify 120 | 121 | sign 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | org.springframework.boot 135 | spring-boot-dependencies 136 | 1.5.4.RELEASE 137 | pom 138 | import 139 | 140 | 141 | 142 | 143 | 144 | 145 | org.projectlombok 146 | lombok 147 | ${lombok.version} 148 | provided 149 | 150 | 151 | org.apache.commons 152 | commons-lang3 153 | ${commons-lang3.version} 154 | 155 | 156 | org.apache.commons 157 | commons-io 158 | ${commons-io.version} 159 | 160 | 161 | org.apache.httpcomponents 162 | httpcore 163 | 164 | 165 | org.apache.httpcomponents 166 | httpclient 167 | 168 | 169 | commons-logging 170 | commons-logging 171 | 172 | 173 | 174 | 175 | org.apache.httpcomponents 176 | fluent-hc 177 | ${fluent-hc.version} 178 | 179 | 180 | commons-logging 181 | commons-logging 182 | 183 | 184 | 185 | 186 | 187 | com.fasterxml.jackson.core 188 | jackson-databind 189 | ${jackson.version} 190 | 191 | 192 | com.fasterxml.jackson.core 193 | jackson-core 194 | ${jackson.version} 195 | 196 | 197 | com.fasterxml.jackson.core 198 | jackson-annotations 199 | ${jackson.version} 200 | 201 | 202 | 203 | org.hibernate 204 | hibernate-validator 205 | 206 | 207 | com.neovisionaries 208 | nv-i18n 209 | ${nv-i18n.version} 210 | 211 | 212 | org.slf4j 213 | slf4j-api 214 | 215 | 216 | org.bouncycastle 217 | bcprov-jdk15on 218 | ${bouncycastle.version} 219 | 220 | 221 | 222 | 223 | org.springframework.boot 224 | spring-boot-starter-web 225 | test 226 | 227 | 228 | org.springframework.boot 229 | spring-boot-starter-test 230 | test 231 | 232 | 233 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/action/Endpoint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.action; 18 | 19 | import com.fasterxml.jackson.core.JsonProcessingException; 20 | import com.fasterxml.jackson.databind.ObjectMapper; 21 | import com.github.woki.payments.adyen.ClientConfig; 22 | import com.github.woki.payments.adyen.model.Card; 23 | import com.github.woki.payments.adyen.model.Error; 24 | import com.github.woki.payments.adyen.model.PaymentRequest; 25 | import com.github.woki.payments.adyen.support.APService; 26 | import org.apache.commons.lang3.StringUtils; 27 | import org.apache.http.HttpEntity; 28 | import org.apache.http.HttpResponse; 29 | import org.apache.http.HttpStatus; 30 | import org.apache.http.StatusLine; 31 | import org.apache.http.client.ResponseHandler; 32 | import org.apache.http.client.fluent.Executor; 33 | import org.apache.http.client.fluent.Request; 34 | import org.apache.http.entity.ContentType; 35 | import org.slf4j.Logger; 36 | import org.slf4j.LoggerFactory; 37 | 38 | import javax.crypto.BadPaddingException; 39 | import javax.crypto.IllegalBlockSizeException; 40 | import java.io.IOException; 41 | import java.io.InputStreamReader; 42 | import java.security.InvalidAlgorithmParameterException; 43 | import java.security.InvalidKeyException; 44 | import java.security.NoSuchAlgorithmException; 45 | import java.util.Date; 46 | import java.util.Map; 47 | 48 | import static org.apache.http.client.fluent.Request.Post; 49 | 50 | /** 51 | * @author Willian Oki <willian.oki@gmail.com> 52 | */ 53 | final class Endpoint { 54 | 55 | private Endpoint() { 56 | // utility 57 | } 58 | 59 | private static final Logger LOG = LoggerFactory.getLogger(Endpoint.class); 60 | private static final ObjectMapper MAPPER = new ObjectMapper(); 61 | 62 | private static Request createPost(APService service, ClientConfig config, Object request) { 63 | Request retval = Post(config.getEndpointPort(service)); 64 | // configure conn timeout 65 | retval.connectTimeout(config.getConnectionTimeout()); 66 | // configure socket timeout 67 | retval.socketTimeout(config.getSocketTimeout()); 68 | // add json 69 | retval.addHeader("Content-Type", "application/json"); 70 | retval.addHeader("Accept", "application/json"); 71 | for (Map.Entry entry : config.getExtraParameters().entrySet()) { 72 | retval.addHeader(entry.getKey(), entry.getValue()); 73 | } 74 | // add content 75 | String bodyString; 76 | try { 77 | bodyString = MAPPER.writeValueAsString(encrypt(config, request)); 78 | } catch (Exception e) { 79 | throw new RuntimeException("CSE/JSON serialization error", e); 80 | } 81 | retval.bodyString(bodyString, ContentType.APPLICATION_JSON); 82 | if (config.hasProxy()) { 83 | retval.viaProxy(config.getProxyHost()); 84 | } 85 | return retval; 86 | } 87 | 88 | private static Executor createExecutor(ClientConfig config) { 89 | Executor retval = Executor.newInstance(); 90 | retval.auth(config.getEndpointHost(), config.getUsername(), config.getPassword()); 91 | if (config.hasProxy() && config.isProxyAuthenticated()) { 92 | retval.auth(config.getProxyHost(), config.getProxyUsername(), config.getProxyPassword()); 93 | } 94 | return retval; 95 | } 96 | 97 | private static Request createRequest(final ClientConfig config, final ReqType request, final Options opts) { 98 | if (LOG.isDebugEnabled()) { 99 | LOG.debug("config: {}, request: {}, options: {}", config, request, opts); 100 | } 101 | Request retval = Endpoint.createPost(APService.from(request, opts), config, request); 102 | if (LOG.isDebugEnabled()) { 103 | LOG.debug("retval: {}", retval); 104 | } 105 | return retval; 106 | } 107 | 108 | @SuppressWarnings("unchecked") 109 | private static ResType handleResponse(final HttpResponse response, final Class responseClass) throws IOException { 110 | ResType retval; 111 | HttpOutcome httpOutcome = handleHttpResponse(response, responseClass); 112 | if (httpOutcome.content != null) { 113 | retval = (ResType) httpOutcome.content; 114 | } else { 115 | if (httpOutcome.statusCode != HttpStatus.SC_OK) { 116 | LOG.warn("{} handling failed: {} - {}", responseClass.getSimpleName(), httpOutcome.statusCode, httpOutcome.message); 117 | } 118 | try { 119 | retval = responseClass.newInstance(); 120 | retval.setStatus(httpOutcome.statusCode); 121 | retval.setMessage(httpOutcome.message); 122 | } catch (InstantiationException | IllegalAccessException e) { 123 | LOG.error("{} instantiation failure", responseClass.getSimpleName()); 124 | throw new IOException(e); 125 | } 126 | } 127 | return retval; 128 | } 129 | 130 | static ResType invoke(final ClientConfig config, final ReqType request, final Class responseClass, 131 | final Options opts) throws IOException { 132 | Request httpRequest = createRequest(config, request, opts); 133 | Executor invoker = createExecutor(config); 134 | return invoker 135 | .execute(httpRequest) 136 | .handleResponse( 137 | new ResponseHandler() { 138 | public ResType handleResponse(HttpResponse response) throws IOException { 139 | ResType res = Endpoint.handleResponse(response, responseClass); 140 | if (LOG.isDebugEnabled()) { 141 | LOG.debug("response: {}", res); 142 | } 143 | return res; 144 | } 145 | } 146 | ); 147 | } 148 | 149 | static ResType invoke(final ClientConfig config, final ReqType request, final Class responseClass) 150 | throws IOException { 151 | return invoke(config, request, responseClass, null); 152 | } 153 | 154 | private static HttpOutcome handleHttpResponse(final HttpResponse response, final Class responseClass) { 155 | final HttpOutcome retval = new HttpOutcome<>(); 156 | final StatusLine status = response.getStatusLine(); 157 | final HttpEntity entity = response.getEntity(); 158 | try { 159 | retval.content = MAPPER.readValue(new InputStreamReader(entity.getContent()), responseClass); 160 | } catch (IOException e) { 161 | LOG.warn("Could no deserialize JSON from entity", e); 162 | } 163 | retval.statusCode = status.getStatusCode(); 164 | switch (status.getStatusCode()) { 165 | case HttpStatus.SC_OK: 166 | retval.message = "Request processed normally"; 167 | break; 168 | case HttpStatus.SC_BAD_REQUEST: 169 | retval.message = "Problem reading or understanding request"; 170 | break; 171 | case HttpStatus.SC_UNPROCESSABLE_ENTITY: 172 | retval.message = "Request validation error"; 173 | break; 174 | case HttpStatus.SC_UNAUTHORIZED: 175 | retval.message = "Authentication required"; 176 | break; 177 | case HttpStatus.SC_FORBIDDEN: 178 | retval.message = "Insufficient permission to process request"; 179 | break; 180 | case HttpStatus.SC_NOT_FOUND: 181 | retval.message = "Service not found"; 182 | break; 183 | default: 184 | retval.message = "Unexpected error"; 185 | } 186 | if (retval.content != null && StringUtils.isEmpty(retval.content.getMessage())) 187 | retval.content.setMessage(retval.message); 188 | return retval; 189 | } 190 | 191 | private static class HttpOutcome { 192 | int statusCode; 193 | String message; 194 | ResType content; 195 | } 196 | 197 | private static Object encrypt(ClientConfig config, Object original) throws BadPaddingException, NoSuchAlgorithmException, IllegalBlockSizeException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalArgumentException, JsonProcessingException { 198 | if (! (original instanceof PaymentRequest)) { 199 | return original; 200 | } 201 | if (StringUtils.isBlank(config.getEncryptionKey())) { 202 | LOG.debug("CSE not enabled"); 203 | return original; 204 | } 205 | Card card = ((PaymentRequest) original).getCard(); 206 | if (card == null) { 207 | LOG.debug("CSE cannot be used: no card to encrypt"); 208 | return original; 209 | } 210 | card.setGenerationtime(new Date()); 211 | String jsonCard = MAPPER.writeValueAsString(card); 212 | String encryptedCard = CSEUtil.encrypt(config.getAesCipher(), config.getRsaCipher(), jsonCard); 213 | ((PaymentRequest) original).setCard(null); 214 | ((PaymentRequest) original).addAdditionalDataEntry(Card.CARD_ENCRYPTED_ADDITIONAL_DATA_KEY_NAME, encryptedCard); 215 | return original; 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Licensed under the Apache License, Version 2.0 (the "License"); 2 | you may not use this file except in compliance with the License. 3 | You may obtain a copy of the License at 4 | 5 | 6 | 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | 13 | ``` 14 | ------------------------------------------------------------------------- 15 | Apache License 16 | Version 2.0, January 2004 17 | http://www.apache.org/licenses/ 18 | 19 | 20 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 21 | 22 | 1. Definitions. 23 | 24 | "License" shall mean the terms and conditions for use, reproduction, 25 | and distribution as defined by Sections 1 through 9 of this document. 26 | 27 | "Licensor" shall mean the copyright owner or entity authorized by 28 | the copyright owner that is granting the License. 29 | 30 | "Legal Entity" shall mean the union of the acting entity and all 31 | other entities that control, are controlled by, or are under common 32 | control with that entity. For the purposes of this definition, 33 | "control" means (i) the power, direct or indirect, to cause the 34 | direction or management of such entity, whether by contract or 35 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 36 | outstanding shares, or (iii) beneficial ownership of such entity. 37 | 38 | "You" (or "Your") shall mean an individual or Legal Entity 39 | exercising permissions granted by this License. 40 | 41 | "Source" form shall mean the preferred form for making modifications, 42 | including but not limited to software source code, documentation 43 | source, and configuration files. 44 | 45 | "Object" form shall mean any form resulting from mechanical 46 | transformation or translation of a Source form, including but 47 | not limited to compiled object code, generated documentation, 48 | and conversions to other media types. 49 | 50 | "Work" shall mean the work of authorship, whether in Source or 51 | Object form, made available under the License, as indicated by a 52 | copyright notice that is included in or attached to the work 53 | (an example is provided in the Appendix below). 54 | 55 | "Derivative Works" shall mean any work, whether in Source or Object 56 | form, that is based on (or derived from) the Work and for which the 57 | editorial revisions, annotations, elaborations, or other modifications 58 | represent, as a whole, an original work of authorship. For the purposes 59 | of this License, Derivative Works shall not include works that remain 60 | separable from, or merely link (or bind by name) to the interfaces of, 61 | the Work and Derivative Works thereof. 62 | 63 | "Contribution" shall mean any work of authorship, including 64 | the original version of the Work and any modifications or additions 65 | to that Work or Derivative Works thereof, that is intentionally 66 | submitted to Licensor for inclusion in the Work by the copyright owner 67 | or by an individual or Legal Entity authorized to submit on behalf of 68 | the copyright owner. For the purposes of this definition, "submitted" 69 | means any form of electronic, verbal, or written communication sent 70 | to the Licensor or its representatives, including but not limited to 71 | communication on electronic mailing lists, source code control systems, 72 | and issue tracking systems that are managed by, or on behalf of, the 73 | Licensor for the purpose of discussing and improving the Work, but 74 | excluding communication that is conspicuously marked or otherwise 75 | designated in writing by the copyright owner as "Not a Contribution." 76 | 77 | "Contributor" shall mean Licensor and any individual or Legal Entity 78 | on behalf of whom a Contribution has been received by Licensor and 79 | subsequently incorporated within the Work. 80 | 81 | 2. Grant of Copyright License. Subject to the terms and conditions of 82 | this License, each Contributor hereby grants to You a perpetual, 83 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 84 | copyright license to reproduce, prepare Derivative Works of, 85 | publicly display, publicly perform, sublicense, and distribute the 86 | Work and such Derivative Works in Source or Object form. 87 | 88 | 3. Grant of Patent License. Subject to the terms and conditions of 89 | this License, each Contributor hereby grants to You a perpetual, 90 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 91 | (except as stated in this section) patent license to make, have made, 92 | use, offer to sell, sell, import, and otherwise transfer the Work, 93 | where such license applies only to those patent claims licensable 94 | by such Contributor that are necessarily infringed by their 95 | Contribution(s) alone or by combination of their Contribution(s) 96 | with the Work to which such Contribution(s) was submitted. If You 97 | institute patent litigation against any entity (including a 98 | cross-claim or counterclaim in a lawsuit) alleging that the Work 99 | or a Contribution incorporated within the Work constitutes direct 100 | or contributory patent infringement, then any patent licenses 101 | granted to You under this License for that Work shall terminate 102 | as of the date such litigation is filed. 103 | 104 | 4. Redistribution. You may reproduce and distribute copies of the 105 | Work or Derivative Works thereof in any medium, with or without 106 | modifications, and in Source or Object form, provided that You 107 | meet the following conditions: 108 | 109 | (a) You must give any other recipients of the Work or 110 | Derivative Works a copy of this License; and 111 | 112 | (b) You must cause any modified files to carry prominent notices 113 | stating that You changed the files; and 114 | 115 | (c) You must retain, in the Source form of any Derivative Works 116 | that You distribute, all copyright, patent, trademark, and 117 | attribution notices from the Source form of the Work, 118 | excluding those notices that do not pertain to any part of 119 | the Derivative Works; and 120 | 121 | (d) If the Work includes a "NOTICE" text file as part of its 122 | distribution, then any Derivative Works that You distribute must 123 | include a readable copy of the attribution notices contained 124 | within such NOTICE file, excluding those notices that do not 125 | pertain to any part of the Derivative Works, in at least one 126 | of the following places: within a NOTICE text file distributed 127 | as part of the Derivative Works; within the Source form or 128 | documentation, if provided along with the Derivative Works; or, 129 | within a display generated by the Derivative Works, if and 130 | wherever such third-party notices normally appear. The contents 131 | of the NOTICE file are for informational purposes only and 132 | do not modify the License. You may add Your own attribution 133 | notices within Derivative Works that You distribute, alongside 134 | or as an addendum to the NOTICE text from the Work, provided 135 | that such additional attribution notices cannot be construed 136 | as modifying the License. 137 | 138 | You may add Your own copyright statement to Your modifications and 139 | may provide additional or different license terms and conditions 140 | for use, reproduction, or distribution of Your modifications, or 141 | for any such Derivative Works as a whole, provided Your use, 142 | reproduction, and distribution of the Work otherwise complies with 143 | the conditions stated in this License. 144 | 145 | 5. Submission of Contributions. Unless You explicitly state otherwise, 146 | any Contribution intentionally submitted for inclusion in the Work 147 | by You to the Licensor shall be under the terms and conditions of 148 | this License, without any additional terms or conditions. 149 | Notwithstanding the above, nothing herein shall supersede or modify 150 | the terms of any separate license agreement you may have executed 151 | with Licensor regarding such Contributions. 152 | 153 | 6. Trademarks. This License does not grant permission to use the trade 154 | names, trademarks, service marks, or product names of the Licensor, 155 | except as required for reasonable and customary use in describing the 156 | origin of the Work and reproducing the content of the NOTICE file. 157 | 158 | 7. Disclaimer of Warranty. Unless required by applicable law or 159 | agreed to in writing, Licensor provides the Work (and each 160 | Contributor provides its Contributions) on an "AS IS" BASIS, 161 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 162 | implied, including, without limitation, any warranties or conditions 163 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 164 | PARTICULAR PURPOSE. You are solely responsible for determining the 165 | appropriateness of using or redistributing the Work and assume any 166 | risks associated with Your exercise of permissions under this License. 167 | 168 | 8. Limitation of Liability. In no event and under no legal theory, 169 | whether in tort (including negligence), contract, or otherwise, 170 | unless required by applicable law (such as deliberate and grossly 171 | negligent acts) or agreed to in writing, shall any Contributor be 172 | liable to You for damages, including any direct, indirect, special, 173 | incidental, or consequential damages of any character arising as a 174 | result of this License or out of the use or inability to use the 175 | Work (including but not limited to damages for loss of goodwill, 176 | work stoppage, computer failure or malfunction, or any and all 177 | other commercial damages or losses), even if such Contributor 178 | has been advised of the possibility of such damages. 179 | 180 | 9. Accepting Warranty or Additional Liability. While redistributing 181 | the Work or Derivative Works thereof, You may choose to offer, 182 | and charge a fee for, acceptance of support, warranty, indemnity, 183 | or other liability obligations and/or rights consistent with this 184 | License. However, in accepting such obligations, You may act only 185 | on Your own behalf and on Your sole responsibility, not on behalf 186 | of any other Contributor, and only if You agree to indemnify, 187 | defend, and hold each Contributor harmless for any liability 188 | incurred by, or claims asserted against, such Contributor by reason 189 | of your accepting any such warranty or additional liability. 190 | 191 | END OF TERMS AND CONDITIONS 192 | ``` 193 | -------------------------------------------------------------------------------- /src/main/java/com/github/woki/payments/adyen/model/PaymentRequestBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Willian Oki 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | package com.github.woki.payments.adyen.model; 18 | 19 | import java.text.DateFormat; 20 | import java.text.SimpleDateFormat; 21 | import java.util.Date; 22 | import java.util.Map; 23 | 24 | /** 25 | * @author Willian Oki <willian.oki@gmail.com> 26 | */ 27 | public final class PaymentRequestBuilder { 28 | private static final DateFormat DELIVERY_DATE_FMTR = new SimpleDateFormat("yyyy-MM-dd'T'00:00:00.000'Z'"); 29 | 30 | private PaymentRequestBuilder() { 31 | // utility 32 | } 33 | 34 | public static IAmount merchantAccount(String account) { 35 | return new Builder(account); 36 | } 37 | 38 | public interface IAmount { 39 | IBuilder amount(Amount amount); 40 | } 41 | 42 | public interface IBuilder { 43 | IBuilder additionalAmount(Amount amount); 44 | 45 | IBuilder additionalDataEntry(String key, String value); 46 | 47 | IBuilder additionalData(Map fields); 48 | 49 | IBuilder bankAccount(BankAccount bankAccount); 50 | 51 | IBuilder billingAddress(Address address); 52 | 53 | IBuilder browserInfo(BrowserInfo info); 54 | 55 | IBuilder browserInfo(String userAgent, String acceptHeader); 56 | 57 | IBuilder captureDelayHours(Integer captureDelayHours); 58 | 59 | IBuilder card(Card card); 60 | 61 | IBuilder dccQuote(ForexQuote dccQuote); 62 | 63 | IBuilder deliveryAddress(Address deliveryAddress); 64 | 65 | IBuilder deliveryDate(Date date); 66 | 67 | IBuilder deviceFingerprint(String deviceFingerprint); 68 | 69 | IBuilder fraudOffset(Long fraudOffset); 70 | 71 | IBuilder installments(Integer value); 72 | 73 | IBuilder mcc(Integer mcc); 74 | 75 | IBuilder merchantOrderReference(String reference); 76 | 77 | IBuilder mpiData(ThreeDSecureData mpiData); 78 | 79 | IBuilder orderReference(String orderReference); 80 | 81 | IBuilder recurring(Recurring recurring); 82 | 83 | IBuilder reference(String reference); 84 | 85 | IBuilder selectedBrand(String brand); 86 | 87 | IBuilder selectedRecurringDetailReference(String selectedRecurringDetailReference); 88 | 89 | IBuilder sessionId(String sessionId); 90 | 91 | IBuilder shopperDateOfBirth(Date dateOfBirth); 92 | 93 | IBuilder shopperEmail(String email); 94 | 95 | IBuilder shopperIP(String ip); 96 | 97 | IBuilder shopperInteraction(ShopperInteraction interaction); 98 | 99 | IBuilder shopperLocale(String shopperLocale); 100 | 101 | IBuilder shopperReference(String shopperReference); 102 | 103 | IBuilder shopperName(Name name); 104 | 105 | IBuilder shopperStatement(String shopperStatement); 106 | 107 | IBuilder shopperSsn(String shopperSsn); 108 | 109 | IBuilder shopperTelephoneNumber(String shopperTelephoneNumber); 110 | 111 | IBuilder shopper(Name name, String email, String ip, String reference, ShopperInteraction interaction); 112 | 113 | IBuilder shopper(Name name, Date birth, String email, String ip, String reference, String ssn, String telephone, ShopperInteraction interaction, String locale, String 114 | statement); 115 | 116 | IBuilder md(String md); 117 | 118 | IBuilder paResponse(String paResponse); 119 | 120 | IBuilder metadata(Map metadata); 121 | 122 | IBuilder store(String store); 123 | 124 | IBuilder totalsGroup(String totalsGroup); 125 | 126 | IBuilder entityType(String entityType); 127 | 128 | IBuilder nationality(String nationality); 129 | 130 | PaymentRequest build(); 131 | } 132 | 133 | private static final class Builder implements IAmount, IBuilder { 134 | private PaymentRequest request; 135 | 136 | Builder(String merchantAccount) { 137 | request = new PaymentRequest(); 138 | request.setMerchantAccount(merchantAccount); 139 | } 140 | 141 | @Override 142 | public IBuilder card(Card card) { 143 | request.setCard(card); 144 | return this; 145 | } 146 | 147 | @Override 148 | public IBuilder shopperDateOfBirth(Date dateOfBirth) { 149 | request.setDateOfBirth(dateOfBirth); 150 | return this; 151 | } 152 | 153 | @Override 154 | public IBuilder dccQuote(ForexQuote dccQuote) { 155 | request.setDccQuote(dccQuote); 156 | return this; 157 | } 158 | 159 | @Override 160 | public IBuilder deliveryAddress(Address deliveryAddress) { 161 | request.setDeliveryAddress(deliveryAddress); 162 | return this; 163 | } 164 | 165 | @Override 166 | public IBuilder reference(String reference) { 167 | request.setReference(reference); 168 | return this; 169 | } 170 | 171 | @Override 172 | public IBuilder shopperEmail(String email) { 173 | request.setShopperEmail(email); 174 | return this; 175 | } 176 | 177 | @Override 178 | public IBuilder shopperIP(String ip) { 179 | request.setShopperIP(ip); 180 | return this; 181 | } 182 | 183 | @Override 184 | public IBuilder shopperReference(String reference) { 185 | request.setShopperReference(reference); 186 | return this; 187 | } 188 | 189 | @Override 190 | public IBuilder shopperInteraction(ShopperInteraction interaction) { 191 | request.setShopperInteraction(interaction); 192 | return this; 193 | } 194 | 195 | @Override 196 | public IBuilder shopperLocale(String shopperLocale) { 197 | request.setShopperLocale(shopperLocale); 198 | return null; 199 | } 200 | 201 | @Override 202 | public IBuilder amount(Amount amount) { 203 | request.setAmount(amount); 204 | return this; 205 | } 206 | 207 | @Override 208 | public PaymentRequest build() { 209 | return request; 210 | } 211 | 212 | @Override 213 | public IBuilder fraudOffset(Long offset) { 214 | request.setFraudOffset(offset); 215 | return this; 216 | } 217 | 218 | @Override 219 | public IBuilder mcc(Integer mcc) { 220 | request.setMcc(mcc); 221 | return this; 222 | } 223 | 224 | @Override 225 | public IBuilder merchantOrderReference(String reference) { 226 | request.setMerchantOrderReference(reference); 227 | return this; 228 | } 229 | 230 | @Override 231 | public IBuilder mpiData(ThreeDSecureData mpiData) { 232 | request.setMpiData(mpiData); 233 | return this; 234 | } 235 | 236 | @Override 237 | public IBuilder orderReference(String orderReference) { 238 | request.setOrderReference(orderReference); 239 | return this; 240 | } 241 | 242 | @Override 243 | public IBuilder recurring(Recurring recurring) { 244 | request.setRecurring(recurring); 245 | return this; 246 | } 247 | 248 | @Override 249 | public IBuilder selectedBrand(String brand) { 250 | request.setSelectedBrand(brand); 251 | return this; 252 | } 253 | 254 | @Override 255 | public IBuilder selectedRecurringDetailReference(String selectedRecurringDetailReference) { 256 | request.setSelectedRecurringDetailReference(selectedRecurringDetailReference); 257 | return this; 258 | } 259 | 260 | @Override 261 | public IBuilder sessionId(String sessionId) { 262 | request.setSessionId(sessionId); 263 | return this; 264 | } 265 | 266 | @Override 267 | public IBuilder additionalDataEntry(String key, String value) { 268 | request.addAdditionalDataEntry(key, value); 269 | return this; 270 | } 271 | 272 | @Override 273 | public IBuilder additionalData(Map fields) { 274 | request.setAdditionalData(fields); 275 | return this; 276 | } 277 | 278 | @Override 279 | public IBuilder bankAccount(BankAccount bankAccount) { 280 | request.setBankAccount(bankAccount); 281 | return this; 282 | } 283 | 284 | @Override 285 | public IBuilder browserInfo(String userAgent, String acceptHeader) { 286 | request.setBrowserInfo(new BrowserInfo(userAgent, acceptHeader)); 287 | return this; 288 | } 289 | 290 | @Override 291 | public IBuilder captureDelayHours(Integer captureDelayHours) { 292 | request.setCaptureDelayHours(captureDelayHours); 293 | return this; 294 | } 295 | 296 | @Override 297 | public IBuilder browserInfo(BrowserInfo info) { 298 | request.setBrowserInfo(info); 299 | return this; 300 | } 301 | 302 | @Override 303 | public IBuilder billingAddress(Address address) { 304 | request.setBillingAddress(address); 305 | return this; 306 | } 307 | 308 | @Override 309 | public IBuilder additionalAmount(Amount amount) { 310 | request.setAdditionalAmount(amount); 311 | return this; 312 | } 313 | 314 | @Override 315 | public IBuilder installments(Integer value) { 316 | request.setInstallments(new Installments(value)); 317 | return this; 318 | } 319 | 320 | @Override 321 | public IBuilder shopperName(Name name) { 322 | request.setShopperName(name); 323 | return this; 324 | } 325 | 326 | @Override 327 | public IBuilder deliveryDate(Date date) { 328 | request.setDeliveryDate(DELIVERY_DATE_FMTR.format(date)); 329 | return this; 330 | } 331 | 332 | @Override 333 | public IBuilder deviceFingerprint(String deviceFingerprint) { 334 | request.setDeviceFingerprint(deviceFingerprint); 335 | return this; 336 | } 337 | 338 | @Override 339 | public IBuilder shopperStatement(String shopperStatement) { 340 | request.setShopperStatement(shopperStatement); 341 | return this; 342 | } 343 | 344 | @Override 345 | public IBuilder shopperSsn(String shopperSsn) { 346 | request.setSocialSecurityNumber(shopperSsn); 347 | return this; 348 | } 349 | 350 | @Override 351 | public IBuilder shopperTelephoneNumber(String shopperTelephoneNumber) { 352 | request.setTelephoneNumber(shopperTelephoneNumber); 353 | return this; 354 | } 355 | 356 | @Override 357 | public IBuilder shopper(Name name, String email, String ip, String reference, ShopperInteraction interaction) { 358 | return shopper(name, null, email, ip, reference, null, null, interaction, null, null); 359 | } 360 | 361 | @Override 362 | public IBuilder shopper(Name name, Date birth, String email, String ip, String reference, String ssn, String telephone, ShopperInteraction interaction, String locale, 363 | String statement) { 364 | request.setShopperName(name); 365 | request.setDateOfBirth(birth); 366 | request.setShopperEmail(email); 367 | request.setShopperIP(ip); 368 | request.setShopperReference(reference); 369 | request.setSocialSecurityNumber(ssn); 370 | request.setTelephoneNumber(telephone); 371 | request.setShopperInteraction(interaction); 372 | request.setShopperLocale(locale); 373 | request.setShopperStatement(statement); 374 | return this; 375 | } 376 | 377 | @Override 378 | public IBuilder md(String md) { 379 | request.setMd(md); 380 | return this; 381 | } 382 | 383 | @Override 384 | public IBuilder paResponse(String paResponse) { 385 | request.setPaResponse(paResponse); 386 | return this; 387 | } 388 | 389 | @Override 390 | public IBuilder metadata(Map metadata) { 391 | request.setMetadata(metadata); 392 | return this; 393 | } 394 | 395 | @Override 396 | public IBuilder store(String store) { 397 | request.setStore(store); 398 | return this; 399 | } 400 | 401 | @Override 402 | public IBuilder totalsGroup(String totalsGroup) { 403 | request.setTotalsGroup(totalsGroup); 404 | return this; 405 | } 406 | 407 | @Override 408 | public IBuilder entityType(String entityType) { 409 | request.setEntityType(entityType); 410 | return this; 411 | } 412 | 413 | @Override 414 | public IBuilder nationality(String nationality) { 415 | request.setNationality(nationality); 416 | return this; 417 | } 418 | } 419 | } 420 | --------------------------------------------------------------------------------