TestsTested | ✗ |
LangLanguage | Obj-CObjective C |
License | MIT |
ReleasedLast Release | Jan 2015 |
Maintained by Lars Steiger.
FastSpring offers an innovative e-commerce engine designed to overcome ease of use, customer service, and cost issues that have plagued software e-commerce companies.
FastSpring's embedded store consists of a controller with some integration points and WebKit's web view. It's thin and very flexible and lets you integrate FastSpring the way that fits best for your application.
To get an idea of how it works, the SDK provides two examples and a test application. All source code is released under the MIT license. It is open to contributions and its use is unrestricted. See RELEASE_NOTES.html for the latest changes.
FsprgEmbeddedStore consists mainly of the FsprgEmbeddedStoreController
and its delegate protocol, FsprgEmbeddedStoreDelegate
.
The FsprgEmbeddedStoreController
controls the connected WebView
(WebKit). It provides functionality to load the store, to monitor the page loading progress and to test if the current connection is secure (https).
@interface FsprgEmbeddedStoreController : NSObject
- (WebView *)webView;
- (void)setWebView:(WebView *)aWebView;
- (id <FsprgEmbeddedStoreDelegate>)delegate;
- (void)setDelegate:(id <FsprgEmbeddedStoreDelegate>)aDelegate;
- (void)loadWithParameters:(FsprgStoreParameters *)parameters;
- (void)loadWithContentsOfFile:(NSString *)aPath;
- (BOOL)isLoading;
- (double)estimatedLoadingProgress;
- (BOOL)isSecure;
- (NSString *)storeHost;
@end
In addition, it has some integration points defined by the FsprgEmbeddedStoreDelegate
protocol. It gives notification of the initial load of the store, of subsequent page loads and of order completion. There's also the possibility to define a view to present the order confirmation to the user.
typedef enum {
FsprgPageFS,
FsprgPagePayPal,
FsprgPageUnknown
} FsprgPageType;
@protocol FsprgEmbeddedStoreDelegate <NSObject>
- (void)didLoadStore:(NSURL *)url;
- (void)didLoadPage:(NSURL *)url ofType:(FsprgPageType)pageType;
- (void)didReceiveOrder:(FsprgOrder *)order;
- (NSView *)viewWithFrame:(NSRect)frame forOrder:(FsprgOrder *)order;
- (void)webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame;
- (void)webView:(WebView *)sender didFailLoadWithError:(NSError *)error forFrame:(WebFrame *)frame;
@end
Use our CocoaPod or perform the following steps.
FsprgEmbeddedStoreMac/FsprgEmbeddedStore
. We suggest to uncheck the option Copy items into destination group's folder
.FsprgEmbeddedStoreMac/FsprgEmbeddedStore/Tests
(in your project) if you don't need FsprgEmbeddedStore unit tests.AppController
and use FsprgEmbeddedStoreController
. Example1.app and Example2.app are showing how to implement an AppController
in detail.AppController
inside Interface Builder.AppController
.@interface AppController : NSObject <FsprgEmbeddedStoreDelegate> {
IBOutlet WebView* storeView;
FsprgEmbeddedStoreController *storeController;
}
- (FsprgEmbeddedStoreController *)storeController;
- (void)setStoreController:(FsprgEmbeddedStoreController *)aStoreController;
- (IBAction)load:(id)sender;
@end
Sometimes users prefer to use a web-store instead of an embedded-store. Use FsprgStoreParameters
to build the web-store URL and open it inside the default browser by using NSWorkspace
.
- (IBAction)openWebStoreInBrowser:(id)sender
{
FsprgStoreParameters *parameters = [FsprgStoreParameters parameters];
[parameters setOrderProcessType:kFsprgOrderProcessDetail];
[parameters setStoreId:@"your_store" withProductId:@"your_product"];
[parameters setMode:kFsprgModeTest];
[[NSWorkspace sharedWorkspace] openURL:[parameters toURL]];
}
The FsprgOrder object represents the order confirmation returned via FsprgEmbeddedStoreDelegate
protocol. To spare you plunging through the headers the following sections contain a real-life example and a compressed API documentation of FsprgOrder and its referred classes.
Here's a real-life example to show the most common case of grabbing the serial number from the fulfilled license. Thanks to Greg Scown from SmileOnMyMac for sharing.
- (void)didReceiveOrder:(FsprgOrder *)order
{
NSEnumerator *e = [[order orderItems] objectEnumerator];
FsprgOrderItem *item = nil;
while (item = [e nextObject]) {
if ([[item productName] hasPrefix:@"MyItemNamePrefix"]) {
NSString *userName = [[item license] licenseName];
NSString *serialNumber = [[item license] firstLicenseCode];
if ([[[item productName] lowercaseString] rangeOfString:@"upgrade"].location != NSNotFound) {
NSLog(@"Upgrade purchase:\nName: %@\nSerial #: %@", userName, serialNumber);
} else {
NSLog(@"Full purchase:\nName: %@\nSerial #: %@", userName, serialNumber);
}
}
}
}
- (BOOL)orderIsTest;
- (NSString *)orderReference;
- (NSString *)orderLanguage;
- (NSString *)orderCurrency;
- (NSNumber *)orderTotal;
- (NSNumber *)orderTotalUSD;
- (NSString *)customerFirstName;
- (NSString *)customerLastName;
- (NSString *)customerCompany;
- (NSString *)customerEmail;
- (FsprgOrderItem *)firstOrderItem; // Shortcut for [[self orderItems] objectAtIndex:0].
- (NSArray *)orderItems;
- (NSString *)productName;
- (NSString *)productDisplay;
- (NSNumber *)quantity;
- (NSNumber *)itemTotal;
- (NSNumber *)itemTotalUSD;
- (NSString *)subscriptionReference; // See https://support.fastspring.com/entries/236487-api-subscriptions
- (NSURL *)subscriptionCustomerURL; // This URL can be presented to the customer to manage their subscription.
- (FsprgFulfillment *)fulfillment;
- (FsprgLicense *)license; // Shortcut for [[self fulfillment] valueForKey:@"license"]
- (FsprgFileDownload *)download; // Shortcut for [[self fulfillment] valueForKey:@"download"]
/*!
* @param aKey type of fulfillment (e.g. license, download)
* @result Specific fulfillment information (FsprgLicense, FsprgFileDownload).
*/
- (id)valueForKey:(NSString *)aKey;
- (NSString *)licenseName;
- (NSString *)licenseEmail;
- (NSString *)licenseCompany;
- (NSString *)firstLicenseCode;
- (NSArray *)licenseCodes;
- (NSDictionary *)licensePropertyList;
- (NSURL *)licenseURL;
- (NSURL *)fileURL;
Example1 app defaults contact fields by accessing MacOS' AddressBook. The order confirmation is a View XIB built inside Interface Builder.
init
FsprgEmbeddedStoreController
on awakeFromNib
load:
to loadWithParameters:
of FsprgEmbeddedStoreController
viewWithFrame:forOrder:
by using a NSViewController
(here OrderViewController
) that uses the View XIB defined inside Interface Builder@implementation AppController
- (id) init
{
self = [super init];
if (self != nil) {
[self setStoreController:[[[FsprgEmbeddedStoreController alloc] init] autorelease]];
[[self storeController] setDelegate:self];
}
return self;
}
- (void)awakeFromNib
{
[[self storeController] setWebView:storeView];
[self load:nil];
}
- (IBAction)load:(id)sender
{
FsprgStoreParameters *parameters = [FsprgStoreParameters parameters];
...
[[self storeController] loadWithParameters:parameters];
}
- (NSView *)viewWithFrame:(NSRect)frame forOrder:(FsprgOrder *)order
{
OrderViewController *orderViewController = [[OrderViewController alloc] initWithNibName:@"OrderView" bundle:nil];
[orderViewController setRepresentedObject:order];
[[orderViewController view] setFrame:frame];
return [orderViewController view];
}
@end
OrderViewController
by extending NSViewController
OrderViewController
FSOrder
) to present order confirmation data to the userExample2 presents the order confirmation by using HTML, CSS and JavaScript. It uses Matt Gemmell's MGTemplateEngine to render the HTML.
The AppController looks like the one in Example1. The only difference is the viewWithFrame:forOrder:
implementation. It uses WebFrame
's loadHTMLString:baseURL:
method to load the HTML and present it to the user.
@implementation AppController
- (NSView *)viewWithFrame:(NSRect)frame forOrder:(FsprgOrder *)order
{
MGTemplateEngine *engine = [MGTemplateEngine templateEngine];
[engine setMatcher:[ICUTemplateMatcher matcherWithTemplateEngine:engine]];
NSString *templatePath = [[NSBundle mainBundle] pathForResource:@"OrderView" ofType:@"html"];
NSDictionary *variables = [NSDictionary dictionaryWithObject:order forKey:@"order"];
NSString *htmlString = [engine processTemplateInFileAtPath:templatePath withVariables:variables];
NSString *templateDirectory = [templatePath substringToIndex:[templatePath length]-[[templatePath lastPathComponent] length]];
NSURL *baseURL = [NSURL URLWithString:[NSString stringWithFormat:@"file://%@", templateDirectory]];
WebFrame *webFrame = [[[WebView alloc] initWithFrame:frame] mainFrame];
[webFrame loadHTMLString:htmlString baseURL:baseURL];
return [webFrame frameView];
}
@end
As we set FsprgOrder
to a variable we can now conveniently access the order information inside the template. The baseURL points to the Resource directory. Thus, we can access CSS files to style the view and JavaScript to add some behavior and nice effects.
<html>
<head>
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />
<title>Your Order</title>
<link rel="stylesheet" type="text/css" href="OrderView.css">
<script src="jquery-1.4.2.js"></script>
<script language="javascript">
$(function() {
$(".orderItemsTitle").fadeIn(2000);
$(".orderItem").fadeIn(2000);
});
</script>
</head>
<body>
<div class="thankYouNote">Thanks for your order {{ order.customerFirstName }}!</div>
<div class="orderItemsTitle">Ordered items</div>
{% for orderItem in order.orderItems %}
<div class="orderItem">
<div class="productName">
{{ orderItem.productName }}
{% if orderItem.quantity > 1 %} ({{ orderItem.quantity }}) {% /if %}
</div>
<div class="licenseKey">Your license key: {{ orderItem.license.firstLicenseCode }}</div>
</div>
{% /for %}
</body>
</html>
The Test application lets you explore FastSpring's parameters and shows you the native order confirmation result (XML plist format).
You can also store that confirmation result as a plist file and load it by using the FsprgEmbeddedStoreController
's loadWithContentsOfFile:
method. It simplifies the development and testing of the order confirmation view.