001/*
002 * Copyright 2010-2013 Ning, Inc.
003 *
004 * Ning licenses this file to you under the Apache License, version 2.0
005 * (the "License"); you may not use this file except in compliance with the
006 * License.  You may obtain a copy of the License at:
007 *
008 *    http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
013 * License for the specific language governing permissions and limitations
014 * under the License.
015 */
016
017package com.ning.billing.recurly;
018
019import com.google.common.base.Joiner;
020import com.google.common.io.BaseEncoding;
021import org.slf4j.Logger;
022import org.slf4j.LoggerFactory;
023
024import javax.crypto.Mac;
025import javax.crypto.SecretKey;
026import javax.crypto.spec.SecretKeySpec;
027import java.util.ArrayList;
028import java.util.List;
029import java.util.UUID;
030
031public class RecurlyJs {
032
033    private static final Logger log = LoggerFactory.getLogger(RecurlyJs.class);
034
035    // Specific to signature generation
036    public static final String PARAMETER_FORMAT = "%s=%s";
037    public static final String PARAMETER_SEPARATOR = "&";
038    public static final String NONCE_PARAMETER = "nonce";
039    public static final String TIMESTAMP_PARAMETER = "timestamp";
040
041    /**
042     * Get Recurly.js Signature
043     * See spec here: http://docs.recurly.com/api/recurlyjs/signatures
044     * <p/>
045     * Returns a signature key for use with recurly.js BuildSubscriptionForm.
046     *
047     * @param privateJsKey recurly.js private key
048     * @return signature string on success, null otherwise
049     */
050    public static String getRecurlySignature(String privateJsKey) {
051        return getRecurlySignature(privateJsKey, new ArrayList<String>());
052    }
053
054    /**
055     * Get Recurly.js Signature
056     * See spec here: http://docs.recurly.com/api/recurlyjs/signatures
057     * <p/>
058     * Returns a signature key for use with recurly.js BuildSubscriptionForm.
059     *
060     * @param privateJsKey recurly.js private key
061     * @param extraParams extra parameters to include in the signature
062     * @return signature string on success, null otherwise
063     */
064    public static String getRecurlySignature(String privateJsKey, List<String> extraParams) {
065        final long unixTime = System.currentTimeMillis() / 1000L;
066        final String uuid = UUID.randomUUID().toString().replaceAll("-", "");
067        return getRecurlySignature(privateJsKey, unixTime, uuid, extraParams);
068    }
069
070    /**
071     * Get Recurly.js Signature with extra parameter strings in the format "[param]=[value]"
072     * See spec here: http://docs.recurly.com/api/recurlyjs/signatures
073     * <p/>
074     * Returns a signature key for use with recurly.js BuildSubscriptionForm.
075     *
076     * @param privateJsKey recurly.js private key
077     * @param unixTime Unix timestamp, i.e. elapsed seconds since Midnight, Jan 1st 1970, UTC
078     * @param nonce A randomly generated string (number used only once)
079     * @param extraParams extra parameters to include in the signature
080     * @return signature string on success, null otherwise
081     */
082    public static String getRecurlySignature(String privateJsKey, Long unixTime, String nonce, List<String> extraParams) {
083        // Mandatory parameters shared by all signatures (as per spec)
084        extraParams = (extraParams == null) ? new ArrayList<String>() : extraParams;
085        extraParams.add(String.format(PARAMETER_FORMAT, TIMESTAMP_PARAMETER, unixTime));
086        extraParams.add(String.format(PARAMETER_FORMAT, NONCE_PARAMETER, nonce));
087        String protectedParams = Joiner.on(PARAMETER_SEPARATOR).join(extraParams);
088
089        return generateRecurlyHMAC(privateJsKey, protectedParams) + "|" + protectedParams;
090    }
091
092    /**
093     * HMAC-SHA1 Hash Generator - Helper method
094     * <p/>
095     * Returns a signature key for use with recurly.js BuildSubscriptionForm.
096     *
097     * @param privateJsKey recurly.js private key
098     * @param protectedParams protected parameter string in the format: &lt;secure_hash&gt;|&lt;protected_string&gt;
099     * @return subscription object on success, null otherwise
100     */
101    private static String generateRecurlyHMAC(String privateJsKey, String protectedParams) {
102        try {
103            SecretKey sk = new SecretKeySpec(privateJsKey.getBytes(), "HmacSHA1");
104            Mac mac = Mac.getInstance("HmacSHA1");
105            mac.init(sk);
106            byte[] result = mac.doFinal(protectedParams.getBytes("UTF-8"));
107            return BaseEncoding.base16().encode(result).toLowerCase();
108        } catch (Exception e) {
109            log.error("Error while trying to generate Recurly HMAC signature", e);
110            return null;
111        }
112    }
113
114}