package com.paymill.models; import java.util.Date; import java.util.List; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonValue; /** * A transaction is the charging of a credit card or a direct debit. In this case you need a new transaction object with either a * valid token, payment, client + payment or preauthorization. Every transaction has a unique identifier which will be generated * by PAYMILL to identify every transaction. You can issue/create, list and display transactions in detail. Refunds can be done in * an extra entity. * @author Vassil Nikolov * @since 3.0.0 */ @JsonIgnoreProperties( ignoreUnknown = true ) public final class Transaction { private String id; private Integer amount; @JsonProperty( "origin_amount" ) private Integer originAmount; private String currency; private Transaction.Status status; @Updateable( "description" ) private String description; private Boolean livemode; private List<Refund> refunds; private Payment payment; private Client client; private Preauthorization preauthorization; @JsonProperty( "created_at" ) private Date createdAt; @JsonProperty( "updated_at" ) private Date updatedAt; @JsonProperty( "response_code" ) private Integer responseCode; @JsonProperty( "short_id" ) private String shortId; @JsonProperty( "is_fraud" ) private Boolean fraud; private List<Fee> fees; @JsonProperty( "app_id" ) private String appId; @JsonProperty( "mandate_reference" ) private String mandateReference; public Transaction() { super(); } public Transaction( final String id ) { this.id = id; } public String getId() { return this.id; } public void setId( final String id ) { this.id = id; } public Integer getAmount() { return this.amount; } public void setAmount( final Integer amount ) { this.amount = amount; } public Integer getOriginAmount() { return this.originAmount; } public void setOriginAmount( final Integer originAmount ) { this.originAmount = originAmount; } public String getCurrency() { return this.currency; } public void setCurrency( final String currency ) { this.currency = currency; } public Transaction.Status getStatus() { return this.status; } public void setStatus( final Transaction.Status status ) { this.status = status; } public String getDescription() { return this.description; } public void setDescription( final String description ) { this.description = description; } public Boolean getLivemode() { return this.livemode; } public void setLivemode( final Boolean livemode ) { this.livemode = livemode; } public List<Refund> getRefunds() { return this.refunds; } public void setRefunds( final List<Refund> refunds ) { this.refunds = refunds; } public Payment getPayment() { return this.payment; } public void setPayment( final Payment payment ) { this.payment = payment; } public Client getClient() { return this.client; } public void setClient( final Client client ) { this.client = client; } public Preauthorization getPreauthorization() { return this.preauthorization; } public void setPreauthorization( final Preauthorization preauthorization ) { this.preauthorization = preauthorization; } public Integer getResponseCode() { return this.responseCode; } public void setResponseCode( final Integer responseCode ) { this.responseCode = responseCode; } /** * Returns the response code as detailed message. * @return the message or <code>null</code> if no message is available. */ public String getResponseCodeDetail() { if( this.responseCode == null ) return null; if( this.responseCode == 10001 ) return "General undefined response"; if( this.responseCode == 10002 ) return "Still waiting on something."; if( this.responseCode == 20000 ) return "General success response."; if( this.responseCode == 40000 ) return "General problem with data."; if( this.responseCode == 40001 ) return "General problem with payment data."; if( this.responseCode == 40100 ) return "Problem with credit card data."; if( this.responseCode == 40101 ) return "Problem with cvv."; if( this.responseCode == 40102 ) return "Card expired or not yet valid."; if( this.responseCode == 40103 ) return "Limit exceeded."; if( this.responseCode == 40104 ) return "Card invalid."; if( this.responseCode == 40105 ) return "Expiry date not valid."; if( this.responseCode == 40106 ) return "Credit card brand required."; if( this.responseCode == 40200 ) return "Problem with bank account data."; if( this.responseCode == 40201 ) return "Bank account data combination mismatch."; if( this.responseCode == 40202 ) return "User authentication failed."; if( this.responseCode == 40300 ) return "Problem with 3d secure data."; if( this.responseCode == 40301 ) return "Currency / amount mismatch"; if( this.responseCode == 40400 ) return "Problem with input data."; if( this.responseCode == 40401 ) return "Amount too low or zero."; if( this.responseCode == 40402 ) return "Usage field too long."; if( this.responseCode == 40403 ) return "Currency not allowed."; if( this.responseCode == 50000 ) return "General problem with backend."; if( this.responseCode == 50001 ) return "Country blacklisted."; if( this.responseCode == 50002 ) return "IP address blacklisted."; if( this.responseCode == 50003 ) return "Anonymous IP proxy used."; if( this.responseCode == 50100 ) return "Technical error with credit card."; if( this.responseCode == 50101 ) return "Error limit exceeded."; if( this.responseCode == 50102 ) return "Card declined by authorization system."; if( this.responseCode == 50103 ) return "Manipulation or stolen card."; if( this.responseCode == 50104 ) return "Card restricted."; if( this.responseCode == 50105 ) return "Invalid card configuration data."; if( this.responseCode == 50200 ) return "Technical error with bank account."; if( this.responseCode == 50201 ) return "Card blacklisted."; if( this.responseCode == 50300 ) return "Technical error with 3D secure."; if( this.responseCode == 50400 ) return "Decline because of risk issues."; if( this.responseCode == 50500 ) return "General timeout."; if( this.responseCode == 50501 ) return "Timeout on side of the acquirer."; if( this.responseCode == 50502 ) return "Risk management transaction timeout."; if( this.responseCode == 50600 ) return "Duplicate transaction."; return null; } /** * Checks if the transaction was successful. Utility method, checks if the response_code is 20000. * @return true if successful, false otherwise */ public boolean isSuccessful() { return this.responseCode != null && this.responseCode == 20000; } public String getShortId() { return this.shortId; } public void setShortId( final String shortId ) { this.shortId = shortId; } public Boolean getFraud() { return this.fraud; } public void setFraud( final Boolean fraud ) { this.fraud = fraud; } public List<Fee> getFees() { return this.fees; } public void setFees( final List<Fee> fees ) { this.fees = fees; } /** * Returns App (ID) that created this transaction or <code>null</code> if created by yourself. * @return {@link String} or <code>null</code>. */ public String getAppId() { return this.appId; } /** * Sets App (ID) that created this transaction or <code>null</code> if created by yourself. * @param appId * {@link String} */ public void setAppId( final String appId ) { this.appId = appId; } /** * Returns the creation date. * @return {@link Date} */ public Date getCreatedAt() { return this.createdAt; } /** * Set the creation date. * @param createdAt * {@link Date} */ @JsonIgnore public void setCreatedAt( final Date createdAt ) { this.createdAt = createdAt; } /** * Set the creation date. * @param seconds * Creation date representation is seconds. */ public void setCreatedAt( final long seconds ) { if( seconds > 0 ) this.createdAt = new Date( seconds * 1000 ); } /** * Returns the last update. * @return {@link Date} */ public Date getUpdatedAt() { return this.updatedAt; } /** * Sets the last update. * @param updatedAt * {@link Date} */ @JsonIgnore public void setUpdatedAt( final Date updatedAt ) { this.updatedAt = updatedAt; } /** * Sets the last update. * @param seconds * Last update representation is seconds. */ public void setUpdatedAt( final long seconds ) { if( seconds > 0 ) this.updatedAt = new Date( seconds * 1000 ); } /** * Set the mandate reference. SEPA mandate reference, can be optionally specified for direct debit transactions. * @param mandateReference * {@link String} */ public void setMandateReference( final String mandateReference ) { this.mandateReference = mandateReference; } /** * Returns the mandate reference. * @return {@link String} */ public String getMandateReference() { return this.mandateReference; } public static Transaction.Filter createFilter() { return new Transaction.Filter(); } public static Transaction.Order createOrder() { return new Transaction.Order(); } public final static class Filter { @SnakeCase( "client" ) private String clientId; @SnakeCase( "payment" ) private String paymentId; @SnakeCase( "amount" ) private String amount; @SnakeCase( "description" ) private String description; @SnakeCase( "created_at" ) private String createdAt; @SnakeCase( "updated_at" ) private String updatedAt; @SnakeCase( "status" ) private String status; private Filter() { super(); } public Transaction.Filter byClientId( final String clientId ) { this.clientId = clientId; return this; } public Transaction.Filter byPaymentId( final String paymentId ) { this.paymentId = paymentId; return this; } public Transaction.Filter byAmount( final int amount ) { this.amount = String.valueOf( amount ); return this; } public Transaction.Filter byAmountGreaterThan( final int amount ) { this.amount = ">" + String.valueOf( amount ); return this; } public Transaction.Filter byAmountLessThan( final int amount ) { this.amount = "<" + String.valueOf( amount ); return this; } public Transaction.Filter byDescription( final String description ) { this.description = description; return this; } /** * Creates filter for createdAt date. If endDate is given the filter is set for range from date to endDate. If endDate is * <code>null</code> the filter search for exact match. * @param date * Start or exact date * @param endDate * End date for the period or <code>null</code>. * @throws IllegalArgumentException * When date is <code>null</code>. * @return {@link Transaction.Filter} object with populated filter for createdAt. */ public Transaction.Filter byCreatedAt( final Date date, final Date endDate ) { this.createdAt = DateRangeBuilder.execute( date, endDate ); return this; } /** * Creates filter for updatedAt date. If endDate is given the filter is set for range from date to endDate. If endDate is * <code>null</code> the filter search for exact match. * @param date * Start or exact date * @param endDate * End date for the period or <code>null</code>. * @throws IllegalArgumentException * When date is <code>null</code>. * @return {@link Transaction.Filter} object with populated filter for updatedAt. */ public Transaction.Filter byUpdatedAt( final Date date, final Date endDate ) { this.updatedAt = DateRangeBuilder.execute( date, endDate ); return this; } public Transaction.Filter byStatus( final Transaction.Status status ) { this.status = status.getValue(); return this; } } public final static class Order { @SnakeCase( "created_at" ) private boolean createdAt; @SnakeCase( value = "asc", order = true ) private boolean asc; @SnakeCase( value = "desc", order = true ) private boolean desc; private Order() { super(); } public Transaction.Order asc() { this.asc = true; this.desc = false; return this; } public Transaction.Order desc() { this.asc = false; this.desc = true; return this; } public Transaction.Order byCreatedAt() { this.createdAt = true; return this; } } public enum Status { OPEN("open"), PENDING("pending"), CLOSED("closed"), FAILED("failed"), PARTIAL_REFUNDED("partial_refunded"), REFUNDED("refunded"), PREAUTH("preauth"), CHARGEBACK("chargeback"), UNDEFINED("undefined"); private String value; private Status( final String value ) { this.value = value; } @JsonValue public String getValue() { return this.value; } @JsonCreator public static Status create( final String value ) { for( Status status : Status.values() ) { if( status.getValue().equals( value ) ) { return status; } } return Status.UNDEFINED; } } }