org.starhope.appius.mb
Class Payment

java.lang.Object
  extended by org.starhope.appius.sql.SQLPeerDatum
      extended by org.starhope.appius.mb.Payment
All Implemented Interfaces:
Serializable, Comparable<Object>

public class Payment
extends SQLPeerDatum

Fairly complete encapsulation of all things related to an individual payment.

Author:
brpocock@star-hope.org, twheys@gmail.com
See Also:
Serialized Form

Nested Class Summary
static class Payment.ItemType
          What type of item is paid-for? Every class implementing Invoiceable should be on this list.
 
Field Summary
private  HashMap<String,String> annotations
           The collection of additional annotations for record-keeping or other purposes.
private  boolean closed
          Once this is set, no further setting is accepted to any internal fields: the payment record is closed out completely.
private  PaymentCredential credentials
          The payment credentials presented.
private  int expiryMonth
          WRITEME
private  int expiryYear
          The year in which the credit-card used to pay for this transaction will expire.
private  Date firstTried
           The time at which we first tried to post this payment.
private  long lastTried
          The time at which we last tried to re-submit a pending transaction
private  String order_code
          The order code is an unique string for this order among all orders placed through a specific source (payment gateway, generally)
private  String order_source
          The order's source code is an identifier which maps to the payment gateway class, but is user-visible as a part of the total payment identifier (source, code, and sequence number)
private  BigDecimal paid
          The amount actually paid with this payment.
private  String payer
          The identification of the payer
private  String paymentFor
          WRITEME
private  PaymentGatewayReal paymentGateway
          Through what payment gateway was this payment processed?
private  BigDecimal price
          The price paid in this payment
private  String resultReason
          The reason for the failure (if it failed), or the authorization code for the success (if it succeeded)
private  int sequence
          The sequence number indicates a series of payments that are related to the same event; this is (always, right now, but) typically a subscription recurring every month.
private static long serialVersionUID
          Java serialisation.
private  Timestamp stamp
          The moment in time at which, per our accounting, this payment took place.
private  boolean success
          Was the payment a success? We keep records of failed payments, too, so it's important to ask that question.
private  boolean testMode
          Was this a test transaction, submitted by the system? It's possible.
private  BigDecimal transactionCode
          The transaction code returned by the payment gateway
private  UserEnrolment userEnrolment
          If this payment was made for an enrolment subscription (right now (TODO) they all are), then this stores the pointer to the UserEnrolment record in question.
private  boolean verified
          True, if the payment has been reconciled or verified.
 
Constructor Summary
Payment()
          Create a payment for which a subscription does not yet exist
Payment(Class<AuthorizeNetGateway> gatewayClass, HashMap<String,String> authorizeNetData)
          Handle Silent Post data returned by Authorize.Net.
Payment(ResultSet resultSet)
          Instantiate a Payment from the results returned by an SQL statement.
Payment(String orderSourceID, String orderCodeString, int paymentSequenceNumber)
          Instantiate a copy of the Payment for a given order and sequence number.
Payment(UserEnrolment subscription)
          Create a new payment upon a given UserEnrolment
 
Method Summary
 void addAnnotation(String key, String value)
          WRITEME: document this method (brpocock@star-hope.org, Jul 13, 2009)
static Payment addPaymentToSequence(UserEnrolment userEnrolment)
          Adds a new payment to the sequence for a User Enrolment.
private  void assertOpen()
           
 void changed()
          WRITEME twheys@gmail.com
 void close()
          Close out the payment completely.
 void flush()
           
 String getAnnotation(String key)
          Retrieve a specific annotation made against a payment.
 String[] getAnnotationNames()
          Retrieve an array of annotation names.
protected  String getCacheUniqueID()
          This is an overriding method.
 PaymentCredential getCredentials()
           Note: Credentials for payment are not saved to JSON or database.
 Currency getCurrency()
          Get the currency with which this payment was/will be made
 int getExpiryMonth()
           
 int getExpiryYear()
           
 BigDecimal getGatewayTransactionCode()
           
static Payment getLastPaymentFor(UserEnrolment userEnrolment)
          Find the last/latest payment made on a specific enrolment
 BigDecimal getPaid()
           
 String getPayer()
           
 String getPayment_for()
          Deprecated. 
 String getPaymentFor()
           
 PaymentGateway getPaymentGateway()
           
 BigDecimal getPrice()
           
 Payment getPriorPayment()
           
 String getResultReason()
           
 Date getRetryTime()
           If this payment is pending, and we're holding credentials in core, we need to figure out how long before we try again.
 int getSequence()
           
 Date getStamp()
           
 Payment getStatusFromGateway()
          FIXME ...
 UserEnrolment getUserEnrolment()
           
private  void init(String orderSourceID, String orderCodeString, int paymentSequenceNumber)
           
 boolean isClosed()
           
 boolean isCompleted()
           
 boolean isSuccess()
           
 boolean isTest()
           
 boolean isVerified()
           
 void prepareForRetry(RetryPaymentException retryPaymentException)
          If a payment should retry processing in future (e.g.
protected  void set(ResultSet rs)
           
 void setCredentials(PaymentCredential paymentCredentials)
           
 void setExpiryMonth(int expiryMonth1)
           
 void setExpiryYear(int expiryYear1)
           
 void setGatewayTransactionCode(BigDecimal transactionCodeNumber)
           
 void setPaid(BigDecimal paid1)
           
 void setPayer(String newPayer)
           
 void setPayment_for(String payment_for1)
          Deprecated. 
 void setPaymentFor(String payment_for1)
           
 void setPaymentGateway(PaymentGatewayReal paymentGatewayReal)
           
 void setPrice(BigDecimal price1)
           
 void setResultReason(String resultReason1)
           
 void setSequence(int sequence1)
           
 void setSuccess(boolean success1)
           Note: Once this routine has been called, most of the setters in this Payment will refuse to operate.
 void setTest(boolean testMode1)
           
 void setVerified(boolean verified1)
           
 boolean shouldRetry()
           
 void shredCredentials()
          Destroy the credentials utterly, once and for all.
 void stamp()
          WRITEME: document this method (brpocock@star-hope.org, Oct 13, 2009)
 org.json.JSONObject toJSON()
           Note, we do not save credentials to any kind of stream or storage (JSON, SQL, &c.).
 String toString()
           
 
Methods inherited from class org.starhope.appius.sql.SQLPeerDatum
compareTo, findInCache, get, saveInCache, set
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
 

Field Detail

serialVersionUID

private static final long serialVersionUID
Java serialisation. Serial version number of interface compatibility

See Also:
Constant Field Values

annotations

private HashMap<String,String> annotations

The collection of additional annotations for record-keeping or other purposes. These are things not used by the system, but which are tracked in case they're useful for human review, auditing, etc.

Annotations are keyed off dotted domain sequences in the same general fashion as Java class names, etc.


closed

private boolean closed
Once this is set, no further setting is accepted to any internal fields: the payment record is closed out completely.


credentials

private transient PaymentCredential credentials
The payment credentials presented. This is usually a credit-card number; or could be some other type of credentials from e.g. a gift card or PayPal or something.


expiryMonth

private int expiryMonth
WRITEME


expiryYear

private int expiryYear
The year in which the credit-card used to pay for this transaction will expire. Actual year value, 2009 = 2009.


firstTried

private transient Date firstTried

The time at which we first tried to post this payment. If a temporary problem was encountered, we can hold on to it (in core) and retry for a while, until a (configured) timeout has been reached.

This field is not persistent.


lastTried

private transient long lastTried
The time at which we last tried to re-submit a pending transaction


order_code

private String order_code
The order code is an unique string for this order among all orders placed through a specific source (payment gateway, generally)


order_source

private String order_source
The order's source code is an identifier which maps to the payment gateway class, but is user-visible as a part of the total payment identifier (source, code, and sequence number)


paid

private BigDecimal paid
The amount actually paid with this payment. This should generally be the same as the price (I can't think of a valid counter-example!)


payer

private String payer
The identification of the payer


paymentFor

private String paymentFor
WRITEME


paymentGateway

private PaymentGatewayReal paymentGateway
Through what payment gateway was this payment processed?


price

private BigDecimal price
The price paid in this payment


resultReason

private String resultReason
The reason for the failure (if it failed), or the authorization code for the success (if it succeeded)


sequence

private int sequence
The sequence number indicates a series of payments that are related to the same event; this is (always, right now, but) typically a subscription recurring every month.


stamp

private Timestamp stamp
The moment in time at which, per our accounting, this payment took place. Note that the user might see a different time on his or her wall clock or credit card statements, &c. There is a degree of ambiguity about when various people consider the transaction to have been completed. Per our purposes, this is that moment.


success

private boolean success
Was the payment a success? We keep records of failed payments, too, so it's important to ask that question.


testMode

private boolean testMode
Was this a test transaction, submitted by the system? It's possible. If this is true, we shouldn't give out anything because of it.


transactionCode

private BigDecimal transactionCode
The transaction code returned by the payment gateway


userEnrolment

private UserEnrolment userEnrolment
If this payment was made for an enrolment subscription (right now (TODO) they all are), then this stores the pointer to the UserEnrolment record in question.


verified

private boolean verified
True, if the payment has been reconciled or verified.

Constructor Detail

Payment

public Payment()
Create a payment for which a subscription does not yet exist


Payment

public Payment(Class<AuthorizeNetGateway> gatewayClass,
               HashMap<String,String> authorizeNetData)
Handle Silent Post data returned by Authorize.Net.

Parameters:
gatewayClass - The class responsible for this payment; should always be AuthorizeNetGateway for now. Provides an easy hook to make sure we're in the right place, not processing e.g. PayPal data here.
authorizeNetData - The key:value pairs of the data from Authorize.Net

Payment

public Payment(ResultSet resultSet)
        throws SQLException
Instantiate a Payment from the results returned by an SQL statement. This allows an arbitrary SELECT to be performed outside of this class, and objects created from the results. Note, even if you are expecting only one return, the JDBC objects start at index -1 in the ResultSet, so you must call resultSet.next () at least once to get the first row.

Parameters:
resultSet - The SQL ResultSet with the cursor currently on the row from which this Payment is to be instantiated.
Throws:
SQLException - if we can't cooperate with the database. Note that we initiate a secondary query on the same connection to obtain annotations, so there is more happening here than just fetching results from the existing ResultSet. If, for example, the connection were closed in between retrieving the ResultSet including this payment and instantiating it, it would throw some SQL exceptions related to that failure.

Payment

public Payment(String orderSourceID,
               String orderCodeString,
               int paymentSequenceNumber)
        throws NotFoundException
Instantiate a copy of the Payment for a given order and sequence number.

Parameters:
orderSourceID - The payment gateway originating the order
orderCodeString - The unique order code
paymentSequenceNumber - The sequence number
Throws:
NotFoundException - if the payment cant't be found.

Payment

public Payment(UserEnrolment subscription)
Create a new payment upon a given UserEnrolment

Parameters:
subscription - WRITEME
Method Detail

addPaymentToSequence

public static Payment addPaymentToSequence(UserEnrolment userEnrolment)
                                    throws NotFoundException
Adds a new payment to the sequence for a User Enrolment.

Parameters:
userEnrolment - The user enrolment to add a payment to.
Returns:
The new Payment.
Throws:
NotFoundException - If the last payment cannot be found.

getLastPaymentFor

public static Payment getLastPaymentFor(UserEnrolment userEnrolment)
                                 throws NotFoundException
Find the last/latest payment made on a specific enrolment

Parameters:
userEnrolment - the enrolment (subscription) object
Returns:
the last/latest payment made, if any
Throws:
NotFoundException - if there are no payments made

addAnnotation

public void addAnnotation(String key,
                          String value)
                   throws AlreadyUsedException
WRITEME: document this method (brpocock@star-hope.org, Jul 13, 2009)

Parameters:
key - The annotation's key (in inverse-dotted notation)
value - The value for this annotation
Throws:
AlreadyUsedException - if the annotation exists
See Also:
annotations

assertOpen

private void assertOpen()
                 throws AlreadyUsedException
Throws:
AlreadyUsedException - if the payment has been posted

changed

public void changed()
WRITEME twheys@gmail.com

Overrides:
changed in class SQLPeerDatum

close

public void close()
           throws SQLException
Close out the payment completely. After setSuccess(boolean), the payment enters a state of “closing out” for post-processing details only to be added to it. Once this routine is called, the payment is totally frozen.

Throws:
SQLException - WRITEME

flush

public void flush()
Specified by:
flush in class SQLPeerDatum
See Also:
SQLPeerDatum.flush()

getAnnotation

public String getAnnotation(String key)
Retrieve a specific annotation made against a payment. Returns "" if the annotation were unset.

Parameters:
key - the ID of the annotation
Returns:
the value

getAnnotationNames

public String[] getAnnotationNames()
Retrieve an array of annotation names.

Returns:
A String array of the names of annotations.

getCacheUniqueID

protected String getCacheUniqueID()
This is an overriding method.

Specified by:
getCacheUniqueID in class SQLPeerDatum
Returns:
The local (Stringified) version of an unique ID; usually the database ID column
See Also:
SQLPeerDatum.getCacheUniqueID()

getCredentials

public PaymentCredential getCredentials()

Note: Credentials for payment are not saved to JSON or database. As such, they only persist as long as they're held in-core.

For this reason, we need to hold on to pending payments' references to avoid potential GC. We cannot re-instantiate a non-completed payment from the database.

This is an intentional security precaution.

Returns:
the credentials used to make the payment.

getCurrency

public Currency getCurrency()
Get the currency with which this payment was/will be made

As with many things, this is still hardcoded as USD.

Returns:
the currency used to make this payment

getExpiryMonth

public int getExpiryMonth()
Returns:
the expiryMonth

getExpiryYear

public int getExpiryYear()
Returns:
the expiryYear

getGatewayTransactionCode

public BigDecimal getGatewayTransactionCode()
Returns:
The transaction code provided by the payment gateway

getPaid

public BigDecimal getPaid()
Returns:
the paid

getPayer

public String getPayer()
Returns:
get the name or other identification provided by the gateway of the payer. (Non-confidential but potentially personally identifiable.)

getPayment_for

@Deprecated
public String getPayment_for()
Deprecated. 

Use getPaymentFor() instead

Returns:
the payment_for

getPaymentFor

public String getPaymentFor()
Returns:
the payment_for

getPaymentGateway

public PaymentGateway getPaymentGateway()
Returns:
The payment gateway used to make this payment

getPrice

public BigDecimal getPrice()
Returns:
the price

getPriorPayment

public Payment getPriorPayment()
                        throws NotFoundException
Returns:
The prior payment in this subscription or other recurring payment arrangement
Throws:
NotFoundException - if there wasn't a prior sequence payment.

getResultReason

public String getResultReason()
Returns:
the failedReason

getRetryTime

public Date getRetryTime()
                  throws AlreadyUsedException,
                         DataException

If this payment is pending, and we're holding credentials in core, we need to figure out how long before we try again. In the spirit of exponential back-off, we'll try every 1 minute for the first 10 minutes, then back off to every 5 minutes for the next 50 minutes, after which we'll go to every 15 minutes until it's been 12 hours, after which time, we'll try every hour until the payment record is dropped.

Note that we need to keep a hard reference to this Payment in the timer routine handling re-submissions, because if the Payment record is purged from the cache, we lose the credentials. This is intentional: we don't want to keep credentials lying around, ever.

Returns:
the date at which we'll next try to complete this payment
Throws:
AlreadyUsedException - if the payment can't be retried
DataException - if there are no payment credentials available for retrying

getSequence

public int getSequence()
Returns:
the sequence

getStamp

public Date getStamp()
Returns:
The date and time at which this payment was made

getStatusFromGateway

public Payment getStatusFromGateway()
FIXME ... this is silly ... ? COMPARE it?

Returns:
verify the payment's information against the transaction gateway

getUserEnrolment

public UserEnrolment getUserEnrolment()
Returns:
The user enrolment for which this payment was made.

init

private void init(String orderSourceID,
                  String orderCodeString,
                  int paymentSequenceNumber)
           throws NotFoundException
Parameters:
orderSourceID - WRITEME
orderCodeString - WRITEME
paymentSequenceNumber - WRITEME
Throws:
NotFoundException - WRITEME

isClosed

public boolean isClosed()
Returns:
true if the payment is closed. See closed

isCompleted

public boolean isCompleted()
Returns:
true if the payment has been completed (successfully or otherwise)

isSuccess

public boolean isSuccess()
Returns:
true if the payment was successful.

isTest

public boolean isTest()
Returns:
true, if this was a test (and not an actual payment)

isVerified

public boolean isVerified()
Returns:
the verified

prepareForRetry

public void prepareForRetry(RetryPaymentException retryPaymentException)
                     throws AlreadyUsedException,
                            DataException
If a payment should retry processing in future (e.g. failure of payment gateway) this is where we enqueue that attempt

Parameters:
retryPaymentException - the exception causing this to be scheduled for a do-over
Throws:
DataException - ...
AlreadyUsedException - ...

set

protected void set(ResultSet rs)
Specified by:
set in class SQLPeerDatum
Parameters:
rs - The result of an SQL query, with the cursor already pointed at the row describing this specific instance of the object.
See Also:
SQLPeerDatum.set(java.sql.ResultSet)

setCredentials

public void setCredentials(PaymentCredential paymentCredentials)
                    throws AlreadyUsedException
Parameters:
paymentCredentials - the credentials to set
Throws:
AlreadyUsedException - if the payment is closed

setExpiryMonth

public void setExpiryMonth(int expiryMonth1)
Parameters:
expiryMonth1 - the expiryMonth to set

setExpiryYear

public void setExpiryYear(int expiryYear1)
Parameters:
expiryYear1 - the expiryYear to set

setGatewayTransactionCode

public void setGatewayTransactionCode(BigDecimal transactionCodeNumber)
                               throws AlreadyUsedException
Parameters:
transactionCodeNumber - the transaction code number returned by the payment gateway
Throws:
AlreadyUsedException - if this payment has already gotten a transaction number

setPaid

public void setPaid(BigDecimal paid1)
Parameters:
paid1 - the amount paid

setPayer

public void setPayer(String newPayer)
Parameters:
newPayer - The payer for this payment

setPayment_for

@Deprecated
public void setPayment_for(String payment_for1)
Deprecated. 

use setPaymentFor(String) instead.

Parameters:
payment_for1 - WRITEME

setPaymentFor

public void setPaymentFor(String payment_for1)
Parameters:
payment_for1 - the payment_for to set

setPaymentGateway

public void setPaymentGateway(PaymentGatewayReal paymentGatewayReal)
                       throws AlreadyUsedException
Parameters:
paymentGatewayReal - Set the payment gateway through which this payment was made
Throws:
AlreadyUsedException - if the payment has already been made

setPrice

public void setPrice(BigDecimal price1)
Parameters:
price1 - the price to set

setResultReason

public void setResultReason(String resultReason1)
Parameters:
resultReason1 - the failedReason to set

setSequence

public void setSequence(int sequence1)
Parameters:
sequence1 - the sequence to set

setSuccess

public void setSuccess(boolean success1)

Note: Once this routine has been called, most of the setters in this Payment will refuse to operate. It will begin closing-out. Only post-transaction setters will work, and they will stop working once close() is called.

This also causes the Payment object to permanently discard the credentials used for payment.

Parameters:
success1 - true if the payment succeeded, false otherwise.

setTest

public void setTest(boolean testMode1)
             throws AlreadyUsedException
Parameters:
testMode1 - If true, then this is meant to be a test transaction, and not a real payment.
Throws:
AlreadyUsedException - if this payment has progressed too far to be marked as a test now

setVerified

public void setVerified(boolean verified1)
Parameters:
verified1 - the verified to set

shouldRetry

public boolean shouldRetry()
Returns:
true, if the engine should retry this transaction

shredCredentials

public void shredCredentials()
Destroy the credentials utterly, once and for all.


stamp

public void stamp()
WRITEME: document this method (brpocock@star-hope.org, Oct 13, 2009)


toJSON

public org.json.JSONObject toJSON()

Note, we do not save credentials to any kind of stream or storage (JSON, SQL, &c.). Likewise, firstTried is useless in future, since it's only useful in concert with credentials for orders that need a retry.

Overrides:
toJSON in class SQLPeerDatum
Returns:
This object's data, serialized into JSON form.
See Also:
SQLPeerDatum.toJSON()

toString

public String toString()
Overrides:
toString in class Object
Returns:
The order source, code, and sequence number of this payment as a string