55
40

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

個人開発のサービスを売買できる「個人開発のフリマ」を作りました

Posted at

title.png

個人開発のフリマは、個人開発やスタートアップで開発したプロダクトをフリマ感覚で売買できるサービスです。

以前、個人開発者の知見を集めたい!「開発会議」を作りましたを投稿させていただき、200名以上の個人開発者の方々にご登録いただき、より良いサービスづくりをしていく為にインタビューを始めました。そのお話をお伺いする中で、自分のサービスで食べていく以外に「サービスの売却」という選択肢を視野に入れていることが分かりました。

また、私自身も以前、開発してバズったサービスを売却しようと考えた時がありました。しかし、従来のWebメディア売買を主要取引としたサイト群では審査に落ちてしまい、結果的に商機を逃してしまったのかなと思っています。

何ができるの

ズバリ、売ったり買ったりです。

開発環境

  • Rails
  • Heroku
  • CloudFlare
  • Amazon s3
  • Amazon SES

売買スキームの実装

今回の売買では決済は実装していません。いずれ実装するかもしれませんが、まずは銀行口座によるマニュアルの振込も想定しています。交渉の申し込みから、契約内容の締結までを説明していきたいと思います。

交渉に関連するモデル

models/user.rb
class User < ApplicationRecord
  has_many :businesses, dependent: :destroy

  has_many :buy_negotiations,  class_name: "Negotiation",
                                  foreign_key: "buyer_id",
                                  dependent: :destroy
  has_many :sell_negotiations, class_name: "Negotiation",
                                  foreign_key: "seller_id",
                                  dependent:   :destroy
  has_many :buy_content, through: :buy_negotiations,  source: :buyer
  has_many :sell_content, through: :sell_negotiations, source: :seller
  has_many :messages
end
models/business.rb
class Business < ApplicationRecord
  belongs_to :user
  has_many :negotiations
end
models/negotiation.rb
class Negotiation < ApplicationRecord
  before_create :set_slug

  belongs_to :business
  has_many :messages

  belongs_to :buyer, class_name: "User"
  belongs_to :seller, class_name: "User"
  
  validates :buyer_id, presence: true
  validates :seller_id, presence: true

  def to_param
    slug
  end

  private

  def set_slug
    loop do
      self.slug = SecureRandom.uuid
      break unless Negotiation.where(slug: slug).exists?
    end
  end
end
models/message.rb
class Message < ApplicationRecord
  belongs_to :user
  belongs_to :negotiation
end

交渉(negotiation)は、URLを念の為スラッグで作るようにしています。

1) 交渉の申し込み

frima.png

views/businesses/show.html.erb
<%= form_with model: @negotiation, url: negotiations_path, local: true do |f| %>
  <%= f.hidden_field :business_id, value: @business.id %>
  <%= f.hidden_field :seller_id, value: @business.user.id %>
  <%= f.hidden_field :buyer_id, value: current_user.id %>
  <%= f.submit "売却の交渉を始める", class: "btn btnPrimary btnStyle" %>
<% end %>
controllers/businesses_controller.rb
  def show
・・・
    @negotiation = @business.negotiations.build
  end

Businessに紐づいた、交渉(negotiation)を作っていきます。

controllers/negotiations_controller.rb
  def create
    @negotiation = Negotiation.create(create_params)
    if @negotiation.save
      NegotiationMailer.send_notification_to_seller(@negotiation).deliver
      flash[:success] = "交渉が始まりました!"
      redirect_to @negotiation
    else
      flash[:alert] = "交渉の開始に失敗しました。"
      redirect_to root_path
    end
  end

交渉が始まったら、売り手側にメールが飛ぶように設定しています。

2) 交渉

frimafrima.png

views/negotiations/show.html.erb
・・・
    <div class="chatBox">
      <%= render partial: 'messages/form', locals: { message: @message, negotiation: @negotiation, negotiation_id: @negotiation.id } %>
    </div>
    <div id="comments_area">
       <%= render partial: 'messages/index', locals: { messages: @messages } %>
    </div>
・・・
views/messages/_form.html.erb
<%= form_with(model: [negotiation, message] ) do |form| %>
  <div class="chatInput">
    <%= form.text_area :content, rows: "3" %>
  </div>
  <div class="chatHead">
    <%= form.submit "送信する" %>
  </div>
<% end %>

views/messages/_index.html.erb
<ul>
  <% messages.each do |message| %>
    <% unless message.id.nil? %>
      <li>
        <div class="msg">
          <div class="flexContainer">
            <div class="icon">
              <%= image_tag message.user.image.to_s %>
            </div>
            <div class="text">
              <div class="name">
                <span><%= message.user.username %></span>
              </div>
              <div class="content">
                <p><%= message.content %></p>
              </div>
            </div>
          </div>
        </div>
      </li>
    <% end %>
  <% end %>
</ul>
views/messages/index.js.erb
$("#comments_area").html("<%= j(render 'index', { messages: @message.negotiation.messages }) %>")
$("textarea").val('')

メッセージのやり取りはリアルタイム性を意識して、Firebaseなどの導入も考えたのですが、最初は実用最低限でいいかなと思って、普通のJSでやりました。

controllers/negotiations_controller.rb
  def show
    @negotiation = Negotiation.find_by_slug(params[:slug])
    @message = Message.new
    @messages = @negotiation.messages
  end
controllers/messages_controller.rb
  def create
    @message = @negotiation.messages.build(create_params)
    @message.user_id = current_user.id
    if @message.save
      MessageMailer.send_notification_to_receiver(@message).deliver
      render :index
    end
  end

negotiationの綴りを何回かタイポして、エラーが起こりました。
モデル名はもっと、なるべくシンプルな方がいいですね。

サービスの使い方はCrieitさんに投稿したのでこちらを参考にしてください。

いずれ、誰かが作るサービスだなと思っていましたが、であれば気づいた自分がまずトライすべきだなと思って、見切りでリリースしていました。すぐに売れることはなくても、とりあえず登録しておけば、忘れた頃に売れるかもしれません!

まずは、自分が作ったサービスに「値付け」から始めてみませんか?

55
40
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
55
40

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?